角 $q,如何链接多个承诺内部和后一个 for 循环

我希望有一个 for 循环,它在每次迭代中调用异步函数。

在 for 循环之后,我想执行另一个代码块,但不是在 for 循环中的所有以前的调用都得到解决之前。

我目前的问题是,for 循环之后的代码块要么在所有异步调用完成之前执行,要么根本不执行。

带 FOR 循环的代码部分和后面的代码块(完整的代码请参阅 小提琴) :

[..]
function outerFunction($q, $scope) {
var defer = $q.defer();
readSome($q,$scope).then(function() {
var promise = writeSome($q, $scope.testArray[0])
for (var i=1; i < $scope.testArray.length; i++) {
promise = promise.then(
angular.bind(null, writeSome, $q, $scope.testArray[i])
);
}
// this must not be called before all calls in for-loop have finished
promise = promise.then(function() {
return writeSome($q, "finish").then(function() {
console.log("resolve");
// resolving here after everything has been done, yey!
defer.resolve();
});
});
});


return defer.promise;
}

我已经创建了一个 jsFiddle,可以在这里找到 http://jsfiddle.net/riemersebastian/B43u6/3/

目前看来执行顺序是正常的(请参阅控制台输出)。

我的猜测是,这仅仅是因为每个函数调用都会立即返回,而不执行任何实际工作。我曾经尝试用 setTimeout 延迟 defer.decision,但是失败了(也就是说,最后一个代码块从未执行过)。您可以在小提琴的注释块中看到它。

当我使用真正的函数写入文件和读取文件时,最后一个代码块是在最后一个写操作完成之前执行的,这不是我想要的。

当然,错误可能出现在其中一个读/写函数中,但是我想确认我在这里发布的代码没有任何错误。

82221 次浏览

您需要使用的是 $q.all,它将许多承诺组合成一个只有在所有承诺都得到解决时才能解决的承诺。

在你的情况下,你可以这样做:

function outerFunction() {


var defer = $q.defer();
var promises = [];


function lastTask(){
writeSome('finish').then( function(){
defer.resolve();
});
}


angular.forEach( $scope.testArray, function(value){
promises.push(writeSome(value));
});


$q.all(promises).then(lastTask);


return defer.promise;
}

使用新的 ES7,你可以以一种更直接的方式得到同样的结果:

let promises =  angular.forEach( $scope.testArray, function(value){
writeSome(value);
});


let results = await Promise.all(promises);


console.log(results);

你可以同时使用 $q和“ reduce”来连接承诺。

function setAutoJoin() {
var deferred = $q.defer(), data;
var array = _.map(data, function(g){
return g.id;
});


function waitTillAllCalls(arr) {
return arr.reduce(function(deferred, email) {
return somePromisingFnWhichReturnsDeferredPromise(email);
}, deferred.resolve('done'));
}


waitTillAllCalls(array);


return deferred.promise;
}

我使用 ES5语法就可以做到这一点

function outerFunction(bookings) {


var allDeferred = $q.defer();
var promises = [];


lodash.map(bookings, function(booking) {
var deferred = $q.defer();


var query = {
_id: booking.product[0].id,
populate: true
}


Stamplay.Object("product").get(query)
.then(function(res) {
booking.product[0] = res.data[0];
deferred.resolve(booking)
})
.catch(function(err) {
console.error(err);
deferred.reject(err);
});


promises.push(deferred.promise);
});


$q.all(promises)
.then(function(results) { allDeferred.resolve(results) })
.catch(function(err) { allDeferred.reject(results) });


return allDeferred.promise;
}