协慌网

登录 贡献 社区

如何访问. then()链中的先前的诺言结果?

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

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?
    });
}

答案

打破链条

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

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

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
    });
}

Promise.all之后的回调中消除参数破坏,所有这些仅在 ES6 中才可用,在 ES5 中, then 调用将由许多 promise 库(QBluebirdwhen ,…)提供的漂亮的助手方法代替.spread(function(resultA, resultB) { …

蓝鸟还具有专用的join功能,可以用更简单(更有效)的结构Promise.all + spread

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

ECMAScript 和声

当然,语言设计者也意识到了这个问题。他们做了很多工作,异步功能提案最终使它成为了

ECMAScript 8

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

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关键字处将执行分段。这些切片可以相互独立,甚至异步地运行 - 这就是我们要在执行下一步之前等待 promise 解析时所要做的。

有专用的库(例如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
});

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

ECMAScript 5

但是,如果您希望 / 需要向后兼容,那么在没有编译器的情况下不能使用它们。当前工具支持生成器功能和异步功能,例如,请参见 Babel 有关生成器异步功能的文档。

然后,还有许多其他专用于 JS 的可编译为 JS 的语言致力于简化异步编程。他们通常使用类似语法await (例如冰的 CoffeeScript ),但也有其他人配备了哈斯克尔样do -notation(如LatteJs一元PureScriptLispyScript )。

同步检查

为变量分配可满足的承诺值,然后通过同步检查获取其值。该示例使用 bluebird 的.value()方法,但是许多库提供了类似的方法。

function getExample() {
    var a = promiseA(…);

    return a.then(function() {
        // some processing
        return promiseB(…);
    }).then(function(resultB) {
        // a is guaranteed to be fulfilled here so we can just retrieve its
        // value synchronously
        var aValue = a.value();
    });
}

您可以将其用于任意多个值:

function getExample() {
    var a = promiseA(…);

    var b = a.then(function() {
        return promiseB(…)
    });

    var c = b.then(function() {
        return promiseC(…);
    });

    var d = c.then(function() {
        return promiseD(…);
    });

    return d.then(function() {
        return a.value() + b.value() + c.value() + d.value();
    });
}