如何在 .then() 链中访问以前的承诺结果?打破枷锁ECMAScript Harmony

我已经将代码重组为承诺,并构建了一个由多个回调组成的长扁平承诺链。最后,我想返回一些复合值,并且需要访问多个中间承诺结果。但是,序列中间的分辨率值不在上一个回调的范围内,我如何访问它们?.then()

function getExample() {
    return promiseA(…).then(function(resultA) {
        // Some processing
        return promiseB(…);
    }).then(function(resultB) {
        // More processing
        return // How do I gain access to resultA here?
    });
}

答案 1

打破枷锁

当您需要访问链中的中间值时,应将链拆分为所需的单个部分。无需附加一个回调并以某种方式尝试多次使用其参数,而是将多个回调附加到同一承诺 - 只要您需要结果值。别忘了,承诺只是代表(代理)未来价值!除了从线性链中的另一个承诺派生一个承诺之外,使用库提供给您的承诺组合器来构建结果值。

这将产生非常简单的控制流程,清晰的功能组合,因此易于模块化。

function getExample() {
    var a = promiseA(…);
    var b = a.then(function(resultA) {
        // some processing
        return promiseB(…);
    });
    return Promise.all([a, b]).then(function([resultA, resultB]) {
        // more processing
        return // something using both resultA and resultB
    });
}

在那之后,回调中的参数解构仅在ES6中才可用,而不是在ES5中,调用将被许多承诺库(QBluebirdwhen,...)提供的漂亮的帮助器方法所取代。Promise.allthen.spread(function(resultA, resultB) { …

Bluebird还具有专用的连接功能,以更简单(更高效)的构造替换+组合:Promise.allspread

…
return Promise.join(a, b, function(resultA, resultB) { … });

答案 2

ECMAScript Harmony

当然,语言设计人员也认识到了这个问题。他们做了很多工作,异步函数提案最终使其成为

ECMAScript 8

您不再需要单个调用或回调函数,因为在异步函数(调用时返回 promise)中,您可以简单地等待 promise 直接解析。它还具有任意控制结构,如条件,循环和try-catch-clauses,但为了方便起见,我们在这里不需要它们:then

async function getExample() {
    var resultA = await promiseA(…);
    // some processing
    var resultB = await promiseB(…);
    // more processing
    return // something using both resultA and resultB
}

ECMAScript 6

当我们在等待ES8时,我们已经使用了一种非常相似的语法。ES6附带了生成器函数,允许在任意放置的关键字处将执行分开。这些切片可以相互依次运行,独立运行,甚至异步运行 - 当我们想要在运行下一步之前等待承诺解决方案时,这就是我们所做的。yield

有专用库(如 cotask.js),但许多 promise 库都有帮助器函数(QBluebirdwhen、...),当你给它们一个产生承诺的生成器函数时,它们会为你执行这个异步的分步执行

var getExample = Promise.coroutine(function* () {
//               ^^^^^^^^^^^^^^^^^ Bluebird syntax
    var resultA = yield promiseA(…);
    // some processing
    var resultB = yield promiseB(…);
    // more processing
    return // something using both resultA and resultB
});

这在 Node 中确实有效.js从 4.0 版本开始,一些浏览器(或其开发版本)确实相对较早地支持生成器语法。

ECMAScript 5

但是,如果您希望/需要向后兼容,则无法在没有转译器的情况下使用它们。当前工具都支持生成器函数和异步函数,例如,请参阅有关生成器异步函数的 Babel 文档。

然后,还有许多其他编译到JS的语言致力于简化异步编程。它们通常使用类似于 的语法(例如 Iced CoffeeScript),但也有其他语法具有类似 Haskell 的 -表示法(例如 LatteJsmonadicPureScriptLispyScript)。awaitdo