即使我编写了随机显示的代码,为什么也保证会先打印所有成功的消息,然后拒绝呢?
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)
});
}
输出
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
答案 0 :(得分:12)
这与异步代码的工作方式有关
.then().catch()
-必须等待两次队列(嗯,我需要解释一下)
.then()
仅一次
从本质上讲,承诺是异步的……在您的代码中,当一个承诺解决时,.then
代码就被放在微任务上了吗?排队...并依次处理
当它拒绝时,因为.then
没有onRejected
回调,那么,您情况下的诺言链.catch
中的下一个处理程序是否被添加到了微任务中?排队-但到那时,所有.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延迟了解析/拒绝
深入的解释
第一件事……您需要了解.then
实际上是
.then(onFullfilled, onRejected)
并返回一个承诺
接下来,.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, 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, true));
p1.then(success, null).then(null, error);
const p2 = new Promise(executorFunction(2, false));
p2.then(success, null).then(null, error);
我们只使用两个诺言,首先拒绝...我们知道它将先输出success 2
然后输出error 1
-即按照我们期望的相反顺序
所以让我们分析发生了什么
因为您要在Promise构造函数executorFunction
中同步解析/拒绝
const p1 = new Promise(executorFunction(1, false));
立即成为已解决的Promise-对于p2实现为2,而对于p1则拒绝为1,但是它从未处于待处理状态。因此,当一个承诺没有待处理(它已经解决,但这意味着已经实现或被拒绝,但是术语已经混在一起,所以我将继续说“未处理”)时,任何“处理程序”都会添加到微任务队列中-因此,在所有这些代码的末尾,微任务队列看起来像
**microtask queue**
(resolved:2).then(success, null).then(null, error); // added second
(rejected:1).then(success, null).then(null, error); // added first
现在JS引擎由于不再运行,因此将处理微任务队列(顺便说一下,队列的头部位于底部)
.then
没有on被拒绝的功能,因此promise值会拖延链条.then
返回带有原始拒绝原因的被拒绝承诺。
**microtask queue**
(rejected:1)>(rejected:1).then(null, error); // added third
(resolved:2).then(success, error).then(null, error); // added second
现在处理下一个微任务
success
success 2
.then
返回一个诺言,因为您的成功函数没有返回值,这是return undefined
并且该诺言被解析为undefined
。
**microtask queue**
(resolved:2)>(resolved:undefined).then(null, error); // added fourth
(rejected:1)>(rejected:1).then(null, error); // added third
error
error 1
返回一个promise,没有处理程序,因此没有任何内容添加到微任务队列中。
.then
现在处理下一个微任务
**microtask queue**
(resolved:2)>(resolved:undefined).then(null, error); // added fourth
返回一个promise,没有处理程序,因此没有任何内容添加到微任务队列中。
.then
为什么使用.then(onFullfilled,onRejected)导致预期顺序
好的,现在,如果我们编写代码
**microtask queue**
**empty**
微任务队列开始,就像
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, true));
p1.then(success, error);
const p2 = new Promise(executorFunction(2, false));
p2.then(success, error);
现在处理下一个微任务
**microtask queue**
(resolved:2).then(success, error); // added second
(rejected:1).then(success, error); // added first
error
error 1
返回一个promise,没有处理程序,因此没有任何内容添加到微任务队列中。
.then
**microtask queue**
(resolved:2).then(success, error); // added second
success
success 2
返回一个promise,没有处理程序,因此没有任何内容添加到微任务队列中。
.then
为什么要按预期顺序添加setTimeout结果
现在让我们更改executorFunction以添加setTimeout
**microtask queue**
**empty**
为简洁起见,再次,我们只使用两个诺言,而第一个诺言失败,因为我们知道原始代码的输出为function executorFunction(num, fail) {
return (resolve, 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, true));
p1.then(success, null).then(null, error);
const p2 = new Promise(executorFunction(2, false));
p2.then(success, null).then(null, error);
,然后为success 2
现在我们要考虑两个队列...微任务和“计时器”-计时器队列的优先级比微任务队列低...即,当没有什么正在运行时(即刻),JS将处理微任务队列直到它为空。甚至尝试计时器队列
所以-接下来我们要去
该代码结尾处有
fail 1
处理计时器队列,我们得到一个微任务** empty microtask queue ** timer queue
setTimeout(resolve(2))
setTimeout(reject(1))
(rejected:1).then(success, null).then(null, error)
哦,微任务队列中有东西,让它处理并忽略计时器队列
** microtask queue ** timer queue
(rejected:1).then(success, null).then(null, error) setTimeout(resolve(2))
没有on被拒绝的功能,因此promise值会拖延链条.then
返回带有原始拒绝原因的被拒绝承诺哦,微任务队列中有东西,让它处理并忽略计时器队列
.then
** microtask queue ** timer queue
(rejected:1).then(success, null).then(null, error) setTimeout(resolve(2))
error
返回一个promise,没有处理程序,因此没有任何内容添加到微任务队列中现在队列看起来像
error 1
所以,我不需要继续,因为.then
在第二个promise链开始之前就已经输出了:p1