Passagem explícita
Semelhante ao aninhamento dos retornos de chamada, essa técnica depende de fechamentos. No entanto, a cadeia permanece plana - em vez de passar apenas o último resultado, algum objeto de estado é passado para cada etapa. Esses objetos de estado acumulam os resultados das ações anteriores, entregando todos os valores que serão necessários mais tarde, mais o resultado da tarefa atual.
function getExample() {
return promiseA(…).then(function(resultA) {
// some processing
return promiseB(…).then(b => [resultA, b]); // function(b) { return [resultA, b] }
}).then(function([resultA, resultB]) {
// more processing
return // something using both resultA and resultB
});
}
Aqui, essa pequena flecha b => [resultA, b]
é a função que se fecha resultA
e passa uma matriz de ambos os resultados para a próxima etapa. Que usa a sintaxe de destruição de parâmetros para dividi-lo em variáveis únicas novamente.
Antes que a desestruturação se tornasse disponível no ES6, um método bacana chamado .spread()
era fornecido por muitas bibliotecas de promessas ( Q , Bluebird , quando ,…). É preciso usar uma função com vários parâmetros - um para cada elemento da matriz - como .spread(function(resultA, resultB) { …
.
Obviamente, esse fechamento necessário aqui pode ser ainda mais simplificado por algumas funções auxiliares, por exemplo
function addTo(x) {
// imagine complex `arguments` fiddling or anything that helps usability
// but you get the idea with this simple one:
return res => [x, res];
}
…
return promiseB(…).then(addTo(resultA));
Como alternativa, você pode empregar Promise.all
para produzir a promessa para a matriz:
function getExample() {
return promiseA(…).then(function(resultA) {
// some processing
return Promise.all([resultA, promiseB(…)]); // resultA will implicitly be wrapped
// as if passed to Promise.resolve()
}).then(function([resultA, resultB]) {
// more processing
return // something using both resultA and resultB
});
}
E você pode não apenas usar matrizes, mas objetos arbitrariamente complexos. Por exemplo, com _.extend
ou Object.assign
em uma função auxiliar diferente:
function augment(obj, name) {
return function (res) { var r = Object.assign({}, obj); r[name] = res; return r; };
}
function getExample() {
return promiseA(…).then(function(resultA) {
// some processing
return promiseB(…).then(augment({resultA}, "resultB"));
}).then(function(obj) {
// more processing
return // something using both obj.resultA and obj.resultB
});
}
Embora esse padrão garanta uma cadeia plana e objetos de estado explícito possam melhorar a clareza, ele se tornará tedioso para uma cadeia longa. Especialmente quando você precisa apenas do estado esporadicamente, ainda precisa passar por cada passo. Com essa interface fixa, os retornos de chamada únicos na cadeia são bastante acoplados e inflexíveis para alterações. Isso dificulta a realização de etapas únicas e os retornos de chamada não podem ser fornecidos diretamente de outros módulos - eles sempre precisam ser agrupados em código padrão que se preocupa com o estado. Funções auxiliares abstratas, como as descritas acima, podem aliviar um pouco a dor, mas sempre estará presente.
javascript
, é relevante em outro idioma. Eu só uso a resposta "quebrar a cadeia" em java e jdeferred