我一直试图等待Angular的$ q的几个承诺,但似乎没有选择'等待所有即使被拒绝'。 我已经创建了一个示例(http://jsfiddle.net/Zenuka/pHEf9/21/),我希望在所有承诺被解析/拒绝时执行一个函数,这可能吗? 类似的东西:
$q.whenAllComplete(promises, function() {....})
编辑:在示例中,您看到第二个服务失败,之后立即执行$q.all().then(..., function(){...})
中的函数。我想等待第五个承诺完成。
答案 0 :(得分:29)
好的,我自己已经实现了一个基本版本(我只想等待一系列的承诺)。任何人都可以扩展这个或创建一个更清洁的版本,如果他们想:-) 检查jsfiddle以查看它的实际效果:http://jsfiddle.net/Zenuka/pHEf9/
angular.module('test').config(['$provide', function ($provide) {
$provide.decorator('$q', ['$delegate', function ($delegate) {
var $q = $delegate;
// Extention for q
$q.allSettled = $q.allSettled || function (promises) {
var deferred = $q.defer();
if (angular.isArray(promises)) {
var states = [];
var results = [];
var didAPromiseFail = false;
if (promises.length === 0) {
deferred.resolve(results);
return deferred.promise;
}
// First create an array for all promises with their state
angular.forEach(promises, function (promise, key) {
states[key] = false;
});
// Helper to check if all states are finished
var checkStates = function (states, results, deferred, failed) {
var allFinished = true;
angular.forEach(states, function (state, key) {
if (!state) {
allFinished = false;
}
});
if (allFinished) {
if (failed) {
deferred.reject(results);
} else {
deferred.resolve(results);
}
}
}
// Loop through the promises
// a second loop to be sure that checkStates is called when all states are set to false first
angular.forEach(promises, function (promise, key) {
$q.when(promise).then(function (result) {
states[key] = true;
results[key] = result;
checkStates(states, results, deferred, didAPromiseFail);
}, function (reason) {
states[key] = true;
results[key] = reason;
didAPromiseFail = true;
checkStates(states, results, deferred, didAPromiseFail);
});
});
} else {
throw 'allSettled can only handle an array of promises (for now)';
}
return deferred.promise;
};
return $q;
}]);
}]);
答案 1 :(得分:17)
类似于all()
如何返回已解析值的数组/散列,来自Kris Kowal的Q的allSettled()
function返回一组看起来像的对象:
{ state: 'fulfilled', value: <resolved value> }
或:
{ state: 'rejected', reason: <rejection error> }
由于这种行为非常方便,我已将该功能移植到Angular.js的$ q:
angular.module('your-module').config(['$provide', function ($provide) {
$provide.decorator('$q', ['$delegate', function ($delegate) {
var $q = $delegate;
$q.allSettled = $q.allSettled || function allSettled(promises) {
// Implementation of allSettled function from Kris Kowal's Q:
// https://github.com/kriskowal/q/wiki/API-Reference#promiseallsettled
var wrapped = angular.isArray(promises) ? [] : {};
angular.forEach(promises, function(promise, key) {
if (!wrapped.hasOwnProperty(key)) {
wrapped[key] = wrap(promise);
}
});
return $q.all(wrapped);
function wrap(promise) {
return $q.when(promise)
.then(function (value) {
return { state: 'fulfilled', value: value };
}, function (reason) {
return { state: 'rejected', reason: reason };
});
}
};
return $q;
}]);
}]);
归功于:
all
implementation 答案 2 :(得分:4)
angularJS中的promise API基于https://github.com/kriskowal/q。我查看了Q
提供的API,它有一个方法allSettled
,但是这个方法还没有通过AngularJS使用的端口公开。这是文档
all函数返回值数组的promise。当这个 承诺得到满足,数组包含了履行的值 原始承诺,与承诺的顺序相同。如果其中之一 如果承诺被拒绝,则返回的承诺立即生效 拒绝,而不是等待批次的其余部分。如果你想等 对于所有承诺要么得到满足,要么被拒绝,你可以 使用allSettled。
答案 3 :(得分:2)
我最近解决了同样的问题。这就是问题所在:
promises
这就是我解决问题的方法:
promises = promises.map(
promise => promise.catch(() => null)
);
$q.all(promises, results => {
// code to handle results
});
它不是一般修复,但它很简单,易于理解。当然,如果您的任何承诺可以解决为null,那么您可以区分拒绝,但它在许多情况下都有效,您可以随时修改catch功能以处理您正在解决的特定问题
答案 4 :(得分:1)
感谢Zenuka的灵感,你可以在https://gist.github.com/JGarrido/8100714
找到我的版本在这里,它处于当前状态:
.config( function($provide) {
$provide.decorator("$q", ["$delegate", function($delegate) {
var $q = $delegate;
$q.allComplete = function(promises) {
if(!angular.isArray(promises)) {
throw Error("$q.allComplete only accepts an array.");
}
var deferred = $q.defer();
var passed = 0;
var failed = 0;
var responses = [];
angular.forEach(promises, function(promise, index) {
promise
.then( function(result) {
console.info('done', result);
passed++;
responses.push(result);
})
.catch( function(result) {
console.error('err', result);
failed++;
responses.push(result);
})
.finally( function() {
if((passed + failed) == promises.length) {
console.log("COMPLETE: " + "passed = " + passed + ", failed = " + failed);
if(failed > 0) {
deferred.reject(responses);
} else {
deferred.resolve(responses);
}
}
})
;
});
return deferred.promise;
};
return $q;
}]);
})
答案 5 :(得分:1)
解决此问题的更简单方法。
$provide.decorator('$q', ['$delegate', function ($delegate) {
var $q = $delegate;
$q.allSettled = $q.allSettled || function (promises) {
var toSettle = [];
if (angular.isArray(promises)) {
angular.forEach(promises, function (promise, key) {
var dfd = $q.defer();
promise.then(dfd.resolve, dfd.resolve);
toSettle.push(dfd.promise);
});
}
return $q.all(toSettle);
};
return $q;
}]);
答案 6 :(得分:0)
一个简单的解决方案是使用catch()来处理任何错误并阻止拒绝传播。您可以通过不从catch()返回值或通过使用错误响应解析然后处理all()中的错误来完成此操作。这样$ q.all()将始终执行。我用一个非常简单的例子更新了小提琴:http://jsfiddle.net/pHEf9/125/
...
function handleError(response) {
console.log('Handle error');
}
// Create 5 promises
var promises = [];
var names = [];
for (var i = 1; i <= 5; i++) {
var willSucceed = true;
if (i == 2) willSucceed = false;
promises.push(
createPromise('Promise' + i, i, willSucceed).catch(handleError));
}
...
请注意,如果您不从catch()中返回值,则传递给all()的已解析promise的数组将包含针对这些错误元素的undefined。
答案 7 :(得分:-3)
最后使用
$q.all(tasks).finally(function() {
// do stuff
});