JavaScript Promise.all 是不是有在成功和失败时触发的回调 [重复]
Posted
技术标签:
【中文标题】JavaScript Promise.all 是不是有在成功和失败时触发的回调 [重复]【英文标题】:Does JavaScript Promise.all have a callback that is fired when there are success AND failures [duplicate]JavaScript Promise.all 是否有在成功和失败时触发的回调 [重复] 【发布时间】:2016-01-03 21:28:02 【问题描述】:我误解了 Promise.all 吗?我在一个数组中有 X 个 promise,我正在尝试汇总数组的成功/失败比率。
以下是我认为我知道的:
Promise.all 接受一组承诺。
如果所有的承诺都成功了,那么.then
回调就会运行。
如果其中一个承诺失败,则调用 .catch
回调,传入的参数是单个引发错误的值。
如果一些成功而一些失败,则不会触发回调,这是所有承诺的结果。 IE。它不能给你一个像(伪代码)[success, fail, success, success]
这样的数组——就像人们期望的那样,并且可以在许多 JS 库(ajax、ember 等)中找到。
就像.then
更像是.success
,而不是一个在所有承诺都实现后总是运行的函数,无论一些成功还是失败。 为什么没有.when
.finally
.runThisShizNoMatterWhat
??还是我错过了什么(很可能)?
【问题讨论】:
您的问题是有道理的,但寻求帮助并同时攻击设计您使用的语言的人似乎适得其反。 成功正是.then(x)
的意义所在。你在想.then(x,y)
。我为 ES6 坚持核心特性而不是走上添加每个人最喜欢的助手的道路而鼓掌。
【参考方案1】:
Promise.all
创建一个新的 Promise,它只能作为一个整体来解决或拒绝。也许您可以将其视为具有 every
数组方法语义,当第一个元素与谓词不匹配时返回 false。
then
函数最多接受两个参数,第二个是被拒绝的处理程序。从这个意义上说,它不仅仅是success
,它实际上可以处理所有情况。 catch
只是一种方便的方法,是.then(undefined, function(reason) ... )
的缩写。
promise API 恐怕没有你需要的东西,你必须自己实现它。
【讨论】:
【参考方案2】:如果其中一个 promise 被拒绝,则 Promise.all
返回的 promise 被拒绝。因此,拒绝处理程序将在其中一个承诺拒绝后立即被调用。如果您只想运行所有 Promise 而不担心拒绝(即,如果有任何 Promise 拒绝,请不要拒绝该 Promise),这可能不是您想要的行为。
您仍然可以处理每个单独的承诺拒绝,以便在拒绝后履行。
var promiseRejected = new Promise(function(resolve, reject)
setTimeout(function()
reject('I was rejected');
, 1000);
);
promiseRejected = promiseRejected.then(null, function(reason)
//I was rejected, now lets fullfill it.
return reason;
);
var promiseResolved = new Promise(function(resolve, reject)
setTimeout(function()
resolve('All good');
, 1500);
);
var time = performance.now();
Promise.all([promiseRejected, promiseResolved]).then(function(results)
//both promises fulfilled; 1500 msecs passed
console.log(results[0], results[1], performance.now() - time);
);
当所有 promise 都被解析/拒绝时解析的 promise 构造函数示例:
Promise.when = function (arrPromises)
if (!Array.isArray(arrPromises))
return new TypeError('Expecting an Array of Promises');
return new Promise(function (resolve, reject)
var len = arrPromises.length,
values = [],
settled = 0;
function settle(value, index)
values[index] = value;
settled++;
if (len === settled)
resolve(values);
if (len === 0)
resolve([]);
else
arrPromises.forEach(function (promise, index)
var handler = function (value)
settle(value, index);
;
promise.then(handler, handler);
);
);
【讨论】:
【参考方案3】:这与 Bluebird Promise.all - multiple promises completed aggregating success and rejections 有关,但这是特定于 Bluebird 的。问题的核心是,如果你想检查某件事是成功还是失败,那么你并不是真的在询问每个承诺的直接结果。相反,您需要在使用 Promise.all
之前转换承诺。这个 ES6 标准的 Promise 没有帮助器,但实现起来很简单。在大多数库中,这称为Promise.settle
。例如
var someThings = [...]; // some list of promises that may succeed or fail
settle(someThings).then(results =>
results.forEach(result =>
if (result.state === 'fullfilled')
console.log('succeeded', result.value);
else
console.log('failed', result.value);
);
);
function settle(arr)
return Promise.all(arr.map(promise =>
return promise.then(
value => (state: 'fullfilled', value),
value => (state: 'rejected', value)
);
));
【讨论】:
【参考方案4】:如果除了价值观之外,你还可以分辨错误,那么做你想做的事就这么简单:
Promise.all(array.map(promise => promise.catch(error => error)))
var log = msg => div.innerhtml += "<p>" + msg + "</p>";
var a = () => Promise.resolve(1);
var b = () => Promise.reject("error");
var c = () => Promise.resolve(3);
Promise.all([a(), b(), c()].map(p => p.catch(e => e))).then(r => log(r));
<div id="div"></div>
【讨论】:
【参考方案5】:从您的问题看来,您似乎希望解决所有的承诺,promise.all
方法不保证,只有方法 promise.settle
保证解决数组中的每个承诺。
如果您想要promise.all
的结果,同时还解决每个promise,并通知哪个promise 已解决或拒绝,那么spex.batch 方法正是您所需要的。
从Batch Processing复制的示例:
var spex = require('spex')(Promise);
// function that returns a promise;
function getWord()
return Promise.resolve("World");
// function that returns a value;
function getExcl()
return '!';
// function that returns another function;
function nested()
return getExcl;
var values = [
123,
"Hello",
getWord,
Promise.resolve(nested)
];
spex.batch(values)
.then(function (data)
console.log("DATA:", data);
, function (reason)
console.log("REASON:", reason);
);
这个输出:
DATA: [ 123, 'Hello', 'World', '!' ]
现在让我们把getWord
改成这样让它失败:
function getWord()
return Promise.reject("World");
现在的输出是:
REASON: [ success: true, result: 123 ,
success: true, result: 'Hello' ,
success: false, result: 'World' ,
success: true, result: '!' ]
即整个数组已解决,报告索引绑定结果。
如果我们不报告全部原因,而是调用getErrors()
:
console.log("REASON:", reason.getErrors());
那么输出将是:
REASON: [ 'World' ]
这只是为了简化对已发生错误列表的快速访问。
您可以从method's protocol 中看到,您可以传入可选的cb
- 回调参数,它将告诉您哪些承诺已解决,哪些承诺被拒绝。
【讨论】:
【参考方案6】:我同意,如果您使用的是 Bluebird,那么使用 Bluebird.reflect 来实现结算是最好的方法。这是另一种可能很好的解决方案,具体取决于您的用例。它在一个有点有趣的情况下对我有用。这也假设 Bluebird 作为 Promise 库。
在我的例子中,我有一个函数数组(“任务”),它们都包装在 Promise.method 中。这些任务可能返回一个被拒绝的承诺,或者它们可能返回或抛出将成为承诺解决方案的普通值。我需要执行所有这些并收集所有结果(不仅仅是第一次失败)。
再次记住,tasks 数组中的每一项都是一个 Promise.method 包装函数。 (易于实施。见http://bluebirdjs.com/docs/api/promise.method.html)
var runTasks = function(tasks)
return Bluebird.reduce(tasks, function (results, task)
return task()
.then(function (result)
results.success.push(result)
return results
)
.caught(function (result)
results.fail.push(result)
return results
)
, success: [], fail: [] )
然后你可以这样调用它,返回一个对象,该对象包含一个已完成值的数组和一个拒绝值的数组:
runTasks(taskArray)
.then(function(results)
// do whatever you want here
// results.success is an array of the returned/resolved values
// results.fail is an array of the rejected/thrown values
)
【讨论】:
Bluebird.attempt(promise)
没有多大意义,是吗?
在我的情况下确实如此,因为并非数组中的每个函数都返回一个承诺。
"不是数组中的每个函数" - 嗯?我以为我们会有一系列的承诺,而不是功能?您的变量名称也表明了这一点。也许你的意思是Promise.resolve
?
抱歉,变量名称令人困惑。我已经编辑了答案以修复它们。在考虑了您的 cmets 之后,我认为我应该对代码进行更改。函数数组是抛出或返回将成为承诺解决方案的值的函数。所以我认为我应该将这些函数包装在 Promise.method 中,然后将 return Bluebird.attempt(task) 替换为 return task()。我将编辑我的答案以提供更多上下文。
是的,或者那样。 Promise.try
(.attempt
) 如果你有 taskArray
很好,但 OP 确实有一系列承诺。以上是关于JavaScript Promise.all 是不是有在成功和失败时触发的回调 [重复]的主要内容,如果未能解决你的问题,请参考以下文章
JavaScript的Promise.all和Promise.race
javascript 迭代数组,知道什么时候完成promise所有promise.all
Javascript 像 Java 8 上的“Promise.all”(可能带有 lambdas)