显式传递
与嵌套回调类似,此技术依赖于闭包。然而,链保持平坦——不是只传递最新的结果,而是每一步都传递一些状态对象。这些状态对象累积先前操作的结果,传递以后再次需要的所有值以及当前任务的结果。
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
});
}
在这里,那个小箭头b => [resultA, b]
是关闭 的函数,resultA
并将两个结果的数组传递给下一步。它使用参数解构语法再次将其分解为单个变量。
在 ES6 提供解构之前.spread()
,许多 Promise 库(Q、Bluebird、when ……)都提供了一个漂亮的辅助方法调用。它需要一个带有多个参数的函数 - 每个数组元素一个 - 用作.spread(function(resultA, resultB) { …
.
当然,这里需要的闭包可以通过一些辅助函数进一步简化,例如
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));
或者,您可以使用Promise.all
生成数组的Promise:
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
});
}
您不仅可以使用数组,还可以使用任意复杂的对象。例如,使用_.extend
或Object.assign
在不同的辅助函数中:
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
});
}
虽然这种模式保证了一个扁平的链并且明确的状态对象可以提高清晰度,但对于长链来说它会变得乏味。特别是当你只是偶尔需要状态时,你仍然需要每一步都通过它。有了这个固定的接口,链中的单个回调就非常紧密地耦合在一起,并且无法灵活改变。它使分解单个步骤变得更加困难,并且不能直接从其他module提供回调——它们总是需要包装在关心状态的样板代码中。像上面这样的抽象辅助函数可以稍微缓解一些痛苦,但它会一直存在。