显式传递
类似于嵌套callback,这种技术依赖于闭包。 然而,链条保持不变 – 而不是只传递最新的结果,每个步骤都会传递一些状态对象。 这些状态对象累积先前操作的结果,将所有稍后需要的值加上当前任务的结果。
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]
是closuresresultA
的函数,并将两个结果的数组传递给下一个步骤。 它使用参数解构语法再次将其分解为单个variables。
在ES6解构之前,许多promise库( Q , Bluebird , when ,…)提供了一个叫做.spread()
的漂亮助手方法。 它使用一个带有多个参数的函数 – 每个数组元素一个 – 用作.spread(function(resultA, resultB) { …
。
当然,这里需要的closures可以通过一些帮助函数进一步简化,例如
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
来产生数组的承诺:
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 }); }
虽然这种模式保证了一个平坦的链条,而明确的状态对象可以提高清晰度,但对于长链来说,这将变得乏味。 尤其是当你只是零星地需要这个状态的时候,你还是要经过每一步。 有了这个固定的接口,链中的单个callback变得相当紧密和不灵活。 它使得更难分解单个步骤,并且callback不能直接从其他模块中提供 – 它们总是需要被封装在关注状态的样板代码中。 像上面这样的抽象帮助函数可以缓解一点痛苦,但是它总是存在的。