我将代码重构为承诺,并构建了一个奇妙的长而平坦的承诺链,由多个.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?
    });
}

当前回答

当使用bluebird时,你可以使用.bind方法来共享承诺链中的变量:

somethingAsync().bind({})
.spread(function (aValue, bValue) {
    this.aValue = aValue;
    this.bValue = bValue;
    return somethingElseAsync(aValue, bValue);
})
.then(function (cValue) {
    return this.aValue + this.bValue + cValue;
});

请点击此链接了解更多信息:

http://bluebirdjs.com/docs/api/promise.bind.html

其他回答

同步检查

将稍后需要的承诺值分配给变量,然后通过同步检查获得它们的值。这个例子使用了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();
    });
}

ECMAScript和谐

当然,语言设计者也认识到了这个问题。他们做了大量的工作,异步函数提案最终得以通过

ECMAScript 8

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

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关键字处将执行分开。这些切片可以彼此独立地、甚至异步地运行——当我们想要在运行下一步之前等待承诺解决时,我们就是这样做的。

有专门的库(如co或task.js),但也有许多承诺库有辅助函数(Q, Bluebird, when,…),当你给它们一个生成承诺的生成器函数时,它们会为你执行异步逐步执行。

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语言 它们致力于简化异步编程。它们通常使用类似于await的语法(例如Iced CoffeeScript),但也有其他一些具有类似haskell的do-notation(例如LatteJs, monadic, PureScript或LispyScript)。

这几天,我也遇到了一些像你一样的问题。最后,我用这个问题找到了一个很好的解决方案,它简单易懂。我希望这能帮助到你。

根据how-to-chain-javascript-promises

好的,让我们看看代码:

const firstPromise = () => {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log('first promise is completed');
            resolve({data: '123'});
        }, 2000);
    });
};

const secondPromise = (someStuff) => {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log('second promise is completed');
            resolve({newData: `${someStuff.data} some more data`});
        }, 2000);
    });
};

const thirdPromise = (someStuff) => {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log('third promise is completed');
            resolve({result: someStuff});
        }, 2000);
    });
};

firstPromise()
    .then(secondPromise)
    .then(thirdPromise)
    .then(data => {
        console.log(data);
    });

解决方案:

你可以使用'bind'显式地将中间值放在任何后来的'then'函数的作用域中。这是一个很好的解决方案,它不需要改变promise的工作方式,只需要一两行代码来传播这些值,就像已经传播错误一样。

下面是一个完整的例子:

// Get info asynchronously from a server
function pGetServerInfo()
    {
    // then value: "server info"
    } // pGetServerInfo

// Write into a file asynchronously
function pWriteFile(path,string)
    {
    // no then value
    } // pWriteFile

// The heart of the solution: Write formatted info into a log file asynchronously,
// using the pGetServerInfo and pWriteFile operations
function pLogInfo(localInfo)
    {
    var scope={localInfo:localInfo}; // Create an explicit scope object
    var thenFunc=p2.bind(scope); // Create a temporary function with this scope
    return (pGetServerInfo().then(thenFunc)); // Do the next 'then' in the chain
    } // pLogInfo

// Scope of this 'then' function is {localInfo:localInfo}
function p2(serverInfo)
    {
    // Do the final 'then' in the chain: Writes "local info, server info"
    return pWriteFile('log',this.localInfo+','+serverInfo);
    } // p2

该解决方案的调用方式如下:

pLogInfo("local info").then().catch(err);

(注意:这个解决方案的一个更复杂和完整的版本已经测试过了,但没有这个示例版本,所以它可能有一个bug。)

另一个答案,使用顺序执行器nsynjs:

function getExample(){

  var response1 = returnPromise1().data;

  // promise1 is resolved at this point, '.data' has the result from resolve(result)

  var response2 = returnPromise2().data;

  // promise2 is resolved at this point, '.data' has the result from resolve(result)

  console.log(response, response2);

}

nynjs.run(getExample,{},function(){
    console.log('all done');
})

更新:增加了工作示例

function synchronousCode() { var urls=[ "https://ajax.googleapis.com/ajax/libs/jquery/1.7.0/jquery.min.js", "https://ajax.googleapis.com/ajax/libs/jquery/1.8.0/jquery.min.js", "https://ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js" ]; for(var i=0; i<urls.length; i++) { var len=window.fetch(urls[i]).data.text().data.length; // ^ ^ // | +- 2-nd promise result // | assigned to 'data' // | // +-- 1-st promise result assigned to 'data' // console.log('URL #'+i+' : '+urls[i]+", length: "+len); } } nsynjs.run(synchronousCode,{},function(){ console.log('all done'); }) <script src="https://rawgit.com/amaksr/nsynjs/master/nsynjs.js"></script>