var errors = 0; var successes = 0; var p1; for (var i = 0; i < 10; i++) { p1 = new Promise(function(resolve,reject) { var num = Math.random(); if (num < .5) { resolve(num); } else { reject(num) } }); p1.then(function success(res) { successes++; console.log("*Success:* " + res) }).catch(function error(error) { errors++ console.log("*Error:* " + error) }); }
OUTPUT
VM331:16 *Success:* 0.28122982053146894 VM331:16 *Success:* 0.30950619874924445 VM331:16 *Success:* 0.4631742111936423 VM331:16 *Success:* 0.059198322061176256 VM331:16 *Success:* 0.17961879374514966 VM331:16 *Success:* 0.24027158041021068 VM331:19 *Error:* 0.9116586303879894 VM331:19 *Error:* 0.7676575145407345 VM331:19 *Error:* 0.5289135948801782 VM331:19 *Error:* 0.5581542856881132
解决方法
.then().catch() – 必须等待队列两次(嗯,我需要解释一下)
.then()只有一次
Promise本质上是异步的…在你的代码中,当一个promise解析时,.then代码放在微任务上?队列…并依次处理
当它拒绝时,因为.then没有onRejected回调,那么,你的情况下的promise链.catch中的下一个处理程序是否被添加到microtask中?队列 – 但到那时,所有.then代码都已执行
尝试使用.then(onSuccess,onError),你会得到你期望的
var errors = 0; var successes = 0; var p1; for (var i = 0; i < 10; i++) { p1 = new Promise(function(resolve,reject) { var num = Math.random(); if (num < .5) { resolve(num); } else { reject(num); } }); p1.then(function success(res) { successes++; console.log("*Success:* " + res); },function error(error) { errors++; console.log("*Error:* " + error); }); }
获得你想要的东西的另一种方式(至少在原生的Promises中)是
var errors = 0; var successes = 0; var p1; for (let i = 0; i < 10; i++) { p1 = new Promise(function(resolve,reject) { setTimeout(() => { var num = Math.random(); if (num < .5) { resolve(`${i} ${num}`); } else { reject(`${i} ${num}`) } }); }); p1.then(function success(res) { successes++; console.log("*Success:* " + res) }).catch(function error(error) { errors++ console.log("* Error:* " + error) }); }
这是因为setTimeout会延迟解析/拒绝
An in-depth explanation
首先要做的事情……你需要了解它.然后才是
.then(onFullfilled,onRejected)
并返回一个Promise
接下来,.catch只是“语法糖”
.then(null,onRejected)
实际上,在大多数Promise库中(在它们出生之前)它被定义为
Promise.prototype.catch = function (onRejected) { return this.then(null,onRejected); };
是的……所以让我们来看看你的代码的一个简单的简单版本 – 为了简洁,只使用三个承诺
function executorFunction(resolve,reject) { const num = Math.random(); if (num < .5) { resolve(num); } else { reject(num) } } let successes = 0,errors = 0; function success(res) { successes++; console.log("*Success:* " + res) } function error(error) { errors++ console.log("*Error:* " + error) } const p1 = new Promise(executorFunction); p1.then(success).catch(error); const p2 = new Promise(executorFunction); p2.then(success).catch(error); const p3 = new Promise(executorFunction); p3.then(success).catch(error);
您可以运行它并看到它产生相同的成功和错误顺序
现在,让我们稍微改变一下,这样我们总能获得成功/失败/成功
function executorFunction(num,fail) { return (resolve,reject) => { if (fail) { reject(num); } else { resolve(num) } }; } function success(res) { console.log("*Success:* " + res) } function error(error) { console.log("*Error:* " + error) } const p1 = new Promise(executorFunction(1,false)); p1.then(success).catch(error); const p2 = new Promise(executorFunction(2,true)); p2.then(success).catch(error); const p3 = new Promise(executorFunction(3,false)); p3.then(success).catch(error);
这总是输出
*Success:* 1 *Success:* 3 *Error:* 2
所以我们看到你在问题中看到的顺序 – 到目前为止一切顺利
现在,让我们以扩展形式重写.then / .catch
function executorFunction(num,true)); p1.then(success,null).then(null,error); const p2 = new Promise(executorFunction(2,false)); p2.then(success,error);
让我们只使用两个承诺,FIRST拒绝…我们知道这将输出成功2然后错误1 – 即以我们期望的相反顺序
那么让我们来分析一下发生了什么
因为您在Promise构造函数executorFunction中同步解析/拒绝
const p1 = new Promise(executorFunction(1,false));
是一个已经解决的Promise – 对于p2已经达到2,并且对于p1被拒绝,原因为1,但它永远不会处于Pending状态.所以,当一个承诺没有待处理(它已经解决,但这意味着要么已经完成或被拒绝,但是术语已经混淆了,所以我会继续说“没有待定”),任何“处理程序”都被添加到微任务队列 – 所以在所有代码的末尾,微任务队列看起来像
**microtask queue** (resolved:2).then(success,error); // added second (rejected:1).then(success,error); // added first
现在JS引擎,因为没有任何运行,处理微任务队列(顺便说一下队列的头部在底部)
>它看到一个被拒绝的承诺(1),但.then没有被拒绝的函数,所以承诺值继承了链
>.然后返回此拒绝的承诺与原始拒绝原因
>这个承诺,因为它有一个处理程序(原始代码中的.catch)被添加到微任务队列中
.
**microtask queue** (rejected:1)>(rejected:1).then(null,error); // added third (resolved:2).then(success,error).then(null,error); // added second
现在处理下一个微任务
>它看到了一个已解决的承诺(2)所以要求成功
>输出成功2
> .then返回一个promise,因为你的success函数没有返回,这是return undefined,promise被解析为undefined
>这个承诺,因为它有一个处理程序(原始代码中的.catch)被添加到微任务队列中
.
**microtask queue** (resolved:2)>(resolved:undefined).then(null,error); // added fourth (rejected:1)>(rejected:1).then(null,error); // added third
>它看到一个被拒绝的promise(1)并且有一个onrejected处理程序调用错误
>输出错误1
> .then返回一个promise,没有处理程序,因此没有任何东西被添加到微任务队列中
.
**microtask queue** (resolved:2)>(resolved:undefined).then(null,error); // added fourth
现在处理下一个微任务
>它看到一个已解决的承诺(2,现在未定义) – 但没有onSuccess处理程序
> .then返回一个promise,因此没有任何东西被添加到微任务队列中
.
**microtask queue** **empty**
why using .then(onFullfilled,onRejected) results in the expected order
好的,现在,如果我们编写代码
function executorFunction(num,reject) => { if (fail) { reject(num); } else { resolve(num) } }; } function success(res) { console.log("*Success:* " + res) } function error(error) { console.log("*Error:* " + error) } const p1 = new Promise(executorFunction(1,error);
微任务队列开始,就像
**microtask queue** (resolved:2).then(success,error); // added first
现在处理下一个微任务
>它看到被拒绝的承诺(1)所以调用错误
>输出错误1
> .then返回一个promise,因此没有任何东西被添加到微任务队列中
.
**microtask queue** (resolved:2).then(success,error); // added second
>它看到了一个已解决的承诺(2)所以要求成功
>输出成功2
> .then返回一个promise,因此没有任何东西被添加到微任务队列中
.
**microtask queue** **empty**
why adding setTimeout results in the expected order
现在让我们更改executorFunction以添加setTimeout
function executorFunction(num,reject) => { setTimeout(function() { if (fail) { reject(num); } else { resolve(num) } }); }; } function success(res) { console.log("*Success:* " + res) } function error(error) { console.log("*Error:* " + error) } const p1 = new Promise(executorFunction(1,error);
同样,为简洁起见,我们只使用两个承诺,第一个失败,因为我们知道原始代码中的输出将成功2然后失败1
现在我们有两个队列要考虑…… microtask和“timer” – 定时器队列的优先级低于微任务队列…即当没有运行(立即)时,JS将处理微任务队列直到它为空甚至尝试计时器队列
所以 – 我们走了
在我们的代码结束时
** empty microtask queue ** timer queue setTimeout(resolve(2)) setTimeout(reject(1))
处理定时器队列,我们得到一个微任务(拒绝:1).然后(成功,空).then(null,error)
** microtask queue ** timer queue (rejected:1).then(success,error) setTimeout(resolve(2))
哦,微任务队列中有一些东西,让我们处理它并忽略定时器队列
>它看到一个被拒绝的承诺(1),因为它有一个处理程序(原始代码中的.catch)被添加到微任务队列中
哦,让我们处理它并忽略定时器队列
** microtask queue ** timer queue (rejected:1).then(success,error) setTimeout(resolve(2))
>它看到一个被拒绝的promise(1)并且有一个onrejected处理程序调用错误
>输出错误1
> .then返回一个promise,因此没有任何东西被添加到微任务队列中
现在队列看起来像
** microtask queue ** timer queue setTimeout(resolve(2))