为什么在微任务队列的末尾执行.catch回调?

我在HTML文件中包含以下代码:

const fooBar = function(resolve,reject) {
	let flag = (Math.round(Math.random() * 10) % 2);
	if(flag)
		resolve({ "value": "foo","rand": Math.random() });
	else
		reject({ "value": "bar","rand": Math.random() });
};
const fooBarSuccess1 = function(value) {
	console.log("Success 1:" + JSON.stringify(value));
};
const fooBarFailure1 = function(value) {
	console.log("Failure 1:" + JSON.stringify(value));
};
const fooBarSuccess2 = function(value) {
	console.log("Success 2:" + JSON.stringify(value));
};
const fooBarFailure2 = function(value) {
	console.log("Failure 2:" + JSON.stringify(value));
};
new Promise(fooBar).then(fooBarSuccess1).catch(fooBarFailure1);
new Promise(fooBar).then(fooBarSuccess2,fooBarFailure2);
console.log("Before setting microTask.");
setTimeout(() => console.log("This Timeout was set before the microTask!"));
queuemicrotask(() => console.log("From microTask!"));
console.log("After setting microTask.");

JSFiddle

当Promise被拒绝时,fooBarFailure1在微任务队列的末尾执行,因此您可能会获得以下输出:

Before setting microTask.
After setting microTask.
Success 2:{"value":"foo","rand":0.3675094508130746}
From microTask!
Failure 1:{"value":"bar","rand":0.6828171208953322}
This Timeout was set before the microTask!

但是,不应该在执行queuemicrotask中的代码之前调用它吗?而且我看不到fooBarFailure2有任何此类问题。它按预期顺序执行。结果与Firefox 71和Google Chrome 78相同。有人可以解释这里发生了什么吗?

lixinnan11 回答:为什么在微任务队列的末尾执行.catch回调?

区别在于fooBarFailure1与根承诺(来自new Promise的承诺)比fooBarFailure2更远。 fooBarFailure1未连接到根承诺,而是连接到.then(fooBarSuccess1)创建的根承诺:

new Promise(fooBar).then(fooBarSuccess1).catch(fooBarFailure1);

相反,fooBarSuccess2fooBarFailure2两者附加到根目录诺言:

new Promise(fooBar).then(fooBarSuccess2,fooBarFailure2);

fooBarFailure1之前的链中有一个内部拒绝处理程序,但是fooBarFailure2是直接挂钩的。这就是导致额外的异步“滴答声”的原因。

让我们看一下故障示例,因为它简化了事情:

const success = function(value) {
    console.log("This never happens");
};
const fooBarFailure1 = function(value) {
    console.log("Failure 1");
};
const fooBarFailure2 = function(value) {
    console.log("Failure 2");
};
Promise.reject().then(success).catch(fooBarFailure1);
Promise.reject().then(success,fooBarFailure2);
console.log("Before setting MicroTask.");
setTimeout(() => console.log("This Timeout was set before the MicroTask!"));
queueMicrotask(() => console.log("From MicroTask!"));
console.log("After setting MicroTask.");

其输出是:

Before setting MicroTask.
After setting MicroTask.
Failure 2
From MicroTask!
Failure 1
This Timeout was set before the MicroTask!

这是为什么:

  • Promise.reject()在两种情况下均返回被拒绝的承诺。
  • Promise.reject().then(success).catch(fooBarFailure1);
    • .then(success)创建一个新的Promise,并挂接实现和拒绝处理程序;拒绝处理程序是内部的,只是传递了拒绝原因,因为没有提供拒绝处理程序。
    • .catch(fooBarFailure1)then的承诺上挂钩了拒绝处理程序。
  • 自从诺言被拒绝以来,它使用微任务来调用附加到它的拒绝处理程序。
  • Promise.reject().then(success,fooBarFailure2);中:
    • then将履行处理程序(success)和拒绝处理程序(fooBarFailure2)挂钩到Promise.reject()的承诺
  • 自从诺言被拒绝以来,它使用微任务来调用附加到它的拒绝处理程序。
  • "Before setting MicroTask."已记录。
  • setTimeout将其任务排队
  • queueMicrotask将其微任务排入队列
  • "After setting MicroTask."已记录。
  • 任务调度已完成,因此微任务处理开始
    • 第一个微任务正在处理Promise.reject().then(success).catch(fooBarFailure1);的拒绝:它拒绝then创建的诺言,排队一个微任务以对返回的诺言then调用拒绝处理程序。
    • li>
    • 第二个微任务正在处理来自Promise.reject().then(success,fooBarFailure2);的拒绝:那拒绝承诺,调用fooBarFailure2
      • "Failure 2"已记录。
    • 第三个微任务是运行queueMicrotask的任务。
      • "From MicroTask!"已记录。
    • 上面预定的对fooBarFailure1的微任务调用运行。
      • "Failure 1"已记录。
  • 下一个任务运行,调用计时器回调
    • "This Timeout was set before the MicroTask!"已记录。
本文链接:https://www.f2er.com/2947832.html

大家都在问