等待所有的承诺解决

所以我有一个情况,我有多个承诺链的未知长度。我希望在处理完所有的 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);
});

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

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

有没有办法等锁链解开?

108228 次浏览

I want the all to resolve when all the chains have been resolved.

Sure, then just pass the promise of each chain into the all() instead of the initial promises:

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

The accepted answer is correct. I would like to provide an example to elaborate it a bit to those who aren't familiar with promise.

Example:

In my example, I need to replace the src attributes of img tags with different mirror urls if available before rendering the content.

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

Explanation:

From AngularJS docs:

The then method:

then(successCallback, errorCallback, notifyCallback) – regardless of when the promise was or will be resolved or rejected, then calls one of the success or error callbacks asynchronously as soon as the result is available. The callbacks are called with a single argument: the result or rejection reason.

$q.all(promises)

Combines multiple promises into a single promise that is resolved when all of the input promises are resolved.

The promises param can be an array of promises.

About bind(), More info here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind

Recently had this problem but with unkown number of promises.Solved using 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;
}

You can use "await" in an "async function".

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

NOTE: I'm not 100% sure you can call an async function from a non-async function and have the right results.

That said this wouldn't ever be used on a website. But for load-testing/integration test...maybe.

Example code:

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