Promise Chains com Node.js

Promessas são uma excelente forma de reduzir as chances de ser exilado para o inferno de callback. Em node.js, tenho usado o promissor módulo Q para lidar com o fluxo de código assíncrono.

Se você tem uma série de coisas que gostaria de fazer em um pedido específico, pode configurar uma cadeia de promessas usando algo como:

first_promise()
.then(function() { return second_promise(); })
.then(function() { return third_promise(); })
...
.then(function() { return nth_promise(); });

onde * funções _promise () contêm algum comportamento e retornam um objeto de promessa que jura eventualmente retornar um resultado. Assim que o resultado real for retornado, ele ativará a próxima função na cadeia.

OK legal. Isso é muito mais fácil de ler do que um monte de callbacks aninhados.

O que acontece se quisermos ativar uma função assíncrona e esperar até obter um resultado antes de executar o comportamento da próxima promessa?

Q resolve isso com promessas adiadas:

function get_the_async_data() {
var deferred = Q.defer();

async_function
(arguments, function(result) {
deferred
.resolve(result);
});

return deferred.promise;
}

OK legal. Agora podemos configurar uma cadeia de eventos assíncronos em que cada um depende da execução do anterior.

E se quisermos disparar um monte de chamadas assíncronas de uma vez e esperar até que todas terminem (em vários momentos, em qualquer ordem) para dar início à próxima promessa na cadeia?

Q fornece um método simples que leva uma série de promessas: Q.all ()

function get_all_the_things(things) {
var the_promises = [];

things
.forEach(function(thing) {
var deferred = Q.defer();
get_a_thing
(thing, function(result) {
deferred
.resolve(result);
});
the_promises
.push(deferred.promise);
});

return Q.all(the_promises);
}

Agora, a próxima função em nossa cadeia irá esperar até que cada promessa adiada que foi criada seja resolvida. Coisa boa.

Por último, podemos querer uma cadeia de promessa baseada em um número variável de operações cuja ordem é importante. Para isso, poderíamos fazer algo assim:

// create an empty promise to begin the chain
var promise_chain = Q.fcall(function(){});

// loop through a variable length list
// of things to process
async_operations
.forEach(function(async_op) {
var promise_link = function() {
var deferred = Q.defer();
perform_async_op
(async_op, function(result) {
deferred
.resolve(result);
});
return deferred.promise;
};

// add the link onto the chain
promise_chain
= promise_chain.then(promise_link);
});

Se você fizer isso dentro de uma função que já faz parte de outra cadeia de promessa, você pode:

return promise_chain;

e a cadeia principal não continuará até que a sub-cadeia de comprimento variável seja resolvida.

Arrumado.