node-postgres라이브러리 위키에는 아래의 예제 코드가 나온다. Transaction으로 insertion을 두 번 하는 코드다.
var Client = require('pg').Client;
var client = new Client(/*your connection info goes here*/);
client.connect();
var rollback = function(client) {
client.query('ROLLBACK', function() {
client.end();
});
};
client.query('BEGIN', function(err, result) {
if(err) return rollback(client);
client.query('INSERT INTO account(money) VALUES(100) WHERE id = $1', [1], function(err, result) {
if(err) return rollback(client);
client.query('INSERT INTO account(money) VALUES(-100) WHERE id = $1', [2], function(err, result) {
if(err) return rollback(client);
//disconnect after successful commit
client.query('COMMIT', client.end.bind(client));
});
});
});
대표적인 콜백헬 모양을 보여준다.
async를 이용한다면 아래와 같이 해볼 수 있을 것 같다.
async.series([
client.query.bind(client, 'BEGIN'),
client.query.bind(client, 'INSERT ... ', [1]),
client.query.bind(client, 'INSERT ... ', [2])
], function(err){
if (err) client.query('ROLLBACK', client.end.bind(client));
else client.query('COMMIT', client.end.bind(client));
});
만약 insert할 데이터가 배열에 들어있다면..
async.series([
client.query.bind(client, 'BEGIN'),
function (cb) {
async.each(values, function (v, cb0) {
client.query("INSERT ... ", v, cb0);
}, cb);
}
], function(err){
if (err) client.query('ROLLBACK', client.end.bind(client));
else client.query('COMMIT', client.end.bind(client));
});
API 하나하나만 봤을 때는 별 문제 없어보이지만 추상화의 부족으로 인해 composition하기가 여전히 불편하다. (cb/cb0를 제대로 사용하기 어렵지 않을까??)
Promise/Observable 등의 추상화가 빛을 발한다. query를 Promise버전으로 만들었다고 보자.
client.query('BEGIN')
.then(function() {
return Promise.all(values.map(function(v) {
return client.query('INSERT ... ', v);
}));
})
.then(client.query.bind(client, 'COMMIT'))
.catch(client.query.bind(client, 'ROLLBACK'))
.finally(client.end.bind(client));
만약 Observable이었다면 all 대신 zip으로, then 대신 flatMap으로, catch 대신 onCompleted Handler로 처리하는 등이 바뀌고 나머진 그대로 일 것이다.