等待所有诺言解决


107

因此,我遇到了多个未知长度的promise链的情况。我希望在处理完所有链条后执行一些操作。那有可能吗?这是一个例子:

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,2,3,它们将在某个随机时间得到解决。然后,我将诺言添加到第一和第三的结尾。我想all在所有链条都解决后解决。这是运行此代码时的输出:

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

有没有办法等待连锁解决?

Answers:


161

当所有链条都解决后,我希望所有人解决。

当然,然后将每个链的承诺传递给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");
});

2
感谢您确认我最担心的事情。现在,我必须想出一种方法来获得最后的承诺。
jensengar

这是什么问题?您的链条是动态构建的吗?
Bergi 2014年

正是我的问题。我试图动态创建一个Promise链,然后在该链完成后想做些事情。
jensengar 2014年

您能否向我们展示您的代码(也许会问一个新问题)?Q.all执行后是否在链子上附加了项-否则应该不重要?
Bergi 2014年

我想向您展示代码...但是我还没有完成编写它,但是我会尽力解释它。我列出了需要执行的“操作”。这些动作可以具有与它们相关联的任何数量的子动作级别。我希望能够在所有动作及其子动作完成后执行某些操作。可能会有多个$q.all,但是一旦我开始解决过程,就不会链接任何新的动作/承诺。
jensengar 2014年

16

接受的答案是正确的。我想提供一个例子,向不熟悉的人详细说明一下promise

例:

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

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(承诺)

在将所有输入的Promise解析后,将多个Promise合并为一个Promise。

promises参数可以是一个承诺数组。

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


then方法$q.all提供了返回的promise的数组,因此您可以循环该数组并then在数组中的每个项目上调用,而不是then在将promise添加到时调用promise_array
尼克

4

最近有这个问题,但是承诺数量未知。使用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/…),但是这种方法有效。
阿纳斯塔西娅


0

您可以在“异步功能”中使用“等待”

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.