等待所有承诺解决

Posted

技术标签:

【中文标题】等待所有承诺解决【英文标题】:Wait for all promises to resolve 【发布时间】:2014-03-12 15:12:29 【问题描述】:

所以我遇到了多个未知长度的承诺链的情况。我希望在处理完所有 CHAINS 后运行一些操作。这甚至可能吗?这是一个例子:

app.controller('MainCtrl', function($scope, $q, $timeout) 
    var one = $q.defer();
    var two = $q.defer();
    var three = $q.defer();

    var all = $q.all([one.promise, two.promise, three.promise]);
    all.then(allSuccess);

    function success(data) 
        console.log(data);
        return data + "Chained";
    

    function allSuccess()
        console.log("ALL PROMISES RESOLVED")
    

    one.promise.then(success).then(success);
    two.promise.then(success);
    three.promise.then(success).then(success).then(success);

    $timeout(function () 
        one.resolve("one done");
    , Math.random() * 1000);

    $timeout(function () 
        two.resolve("two done");
    , Math.random() * 1000);

    $timeout(function () 
        three.resolve("three done");
    , Math.random() * 1000);
);

在这个例子中,我为承诺一、二和三设置了一个$q.all(),它们将在某个随机时间得到解决。然后我在 1 和 3 的末尾添加承诺。我希望all 在所有链都已解决后解决。这是我运行此代码时的输出:

one done 
one doneChained
two done
three done
ALL PROMISES RESOLVED
three doneChained
three doneChainedChained 

有没有办法等待链解决?

【问题讨论】:

【参考方案1】:

我希望在所有链都解决后全部解决。

当然,那么只需将每个链的承诺传递给all(),而不是最初的承诺:

$q.all([one.promise, two.promise, three.promise]).then(function() 
    console.log("ALL INITIAL PROMISES RESOLVED");
);

var onechain   = one.promise.then(success).then(success),
    twochain   = two.promise.then(success),
    threechain = three.promise.then(success).then(success).then(success);

$q.all([onechain, twochain, threechain]).then(function() 
    console.log("ALL PROMISES RESOLVED");
);

【讨论】:

感谢您确认我最害怕的事情。现在我必须想出一个方法来获得最后的承诺,哈哈。 这有什么问题?您的链是动态构建的吗? 正是我的问题。我正在尝试动态创建一个承诺链,然后我想在链完成时做一些事情。 你能告诉我们你的代码吗(也许问一个新问题)? Q.all 执行后是否有项目附加到链中 - 否则应该是微不足道的? 我很乐意向您展示代码...但我还没有完成编写它,但我会尽力解释它。我有一份需要完成的“行动”清单。这些动作可以有任意数量的与其相关联的子动作级别。当所有动作及其子动作都完成时,我希望能够做某事。可能会有多个$q.alls,但是一旦我开始解决过程,就不会链接任何新的操作/承诺。【参考方案2】:

accepted answer 是正确的。我想举个例子给不熟悉promise的人稍微说明一下。

示例:

在我的示例中,我需要在呈现内容之前将img 标签的src 属性替换为不同的镜像url(如果可用)。

var img_tags = content.querySelectorAll('img');

function checkMirrorAvailability(url) 

    // blah blah 

    return promise;


function changeSrc(success, y, response) 
    if (success === true) 
        img_tags[y].setAttribute('src', response.mirror_url);
     
    else 
        console.log('No mirrors for: ' + img_tags[y].getAttribute('src'));
    


var promise_array = [];

for (var y = 0; y < img_tags.length; y++) 
    var img_src = img_tags[y].getAttribute('src');

    promise_array.push(
        checkMirrorAvailability(img_src)
        .then(

            // a callback function only accept ONE argument. 
            // Here, we use  `.bind` to pass additional arguments to the
            // callback function (changeSrc).

            // successCallback
            changeSrc.bind(null, true, y),
            // errorCallback
            changeSrc.bind(null, false, y)
        )
    );


$q.all(promise_array)
.then(
    function() 
        console.log('all promises have returned with either success or failure!');
        render(content);
    
    // We don't need an errorCallback function here, because above we handled
    // all errors.
);

说明:

来自 AngularJS docs:

then 方法:

then(successCallback, errorCallback, notifyCallback) – 无论承诺何时被解决或被拒绝,然后调用 一旦成功或错误回调之一异步 结果可用。使用 单次调用回调 论据:结果或拒绝原因。

$q.all(承诺)

将多个承诺组合成一个承诺,当 所有输入承诺都已解决。

promises 参数可以是一组承诺。

关于bind(),更多信息在这里:https://developer.mozilla.org/en-US/docs/Web/javascript/Reference/Global_Objects/Function/bind

【讨论】:

$q.allthen 方法提供了一个返回的承诺数组,因此您可以循环该数组并在数组中的每个项目上调用 then,而不是调用 @987654334 @当你将承诺添加到promise_array时。【参考方案3】:

最近遇到了这个问题,但 promise 的数量未知。使用 jQuery.map() 解决。

function methodThatChainsPromises(args) 

    //var args = [
    //    'myArg1',
    //    'myArg2',
    //    'myArg3',
    //];

    var deferred = $q.defer();
    var chain = args.map(methodThatTakeArgAndReturnsPromise);

    $q.all(chain)
    .then(function () 
        $log.debug('All promises have been resolved.');
        deferred.resolve();
    )
    .catch(function () 
        $log.debug('One or more promises failed.');
        deferred.reject();
    );

    return deferred.promise;

【讨论】:

不是 jQuery.map() 而是 Array.prototype.map() (developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…) 但这种方法有效。【参考方案4】:

有办法。 $q.all(...

您可以检查以下内容:

http://jsfiddle.net/ThomasBurleson/QqKuk/

http://denisonluz.com/blog/index.php/2013/10/06/angularjs-returning-multiple-promises-at-once-with-q-all/

【讨论】:

这需要我知道我的链条的长度,对吧?我的意思是,如果我有一个长度为 10 的承诺,我将不得不这样做 $q.all([p1.then(..).then(...).then(...).then(...) ...]); 对吧?【参考方案5】:

您可以使用"await" in an "async function"。

app.controller('MainCtrl', async function($scope, $q, $timeout) 
  ...
  var all = await $q.all([one.promise, two.promise, three.promise]); 
  ...

注意:我不能 100% 确定您可以从非异步函数调用异步函数并获得正确的结果。

也就是说,这永远不会在网站上使用。但是对于负载测试/集成测试......也许吧。

示例代码:

async function waitForIt(printMe) 
  console.log(printMe);
  console.log("..."+await req());
  console.log("Legendary!")


function req() 
  
  var promise = new Promise(resolve => 
    setTimeout(() => 
      resolve("DARY!");
    , 2000);
    
  );

    return promise;


waitForIt("Legen-Wait For It");

【讨论】:

以上是关于等待所有承诺解决的主要内容,如果未能解决你的问题,请参考以下文章

在更新状态和重定向之前等待所有承诺解决

承诺等待得到解决而不返回

打字稿 - 在函数返回之前等待承诺解决

在等待时,未能同时解决的发电机产生的承诺

等待/异步如何处理未解决的承诺

等待但从未解决/拒绝承诺内存使用[重复]