简体   繁体   English

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

[英]Why are .catch callbacks executed at the end of the microtask queue?

I have the following code in an HTML file:我在 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 JSFiddle

When the Promise gets rejected, fooBarFailure1 is executed at the end of the microtask queue, so you might get the below output:当 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!

However, shouldn't it be invoked before the code inside queueMicrotask is executed?但是,是不是应该在queueMicrotask里面的代码执行之前调用呢? And I don't see any such issues with fooBarFailure2 .而且我没有看到fooBarFailure2任何此类问题。 It gets executed in the expected order.它以预期的顺序执行。 The result is the same in Firefox 71 and Google Chrome 78. Can anybody explain what's happening here?结果在 Firefox 71 和 Google Chrome 78 中是一样的。谁能解释一下这里发生了什么?

The difference is that fooBarFailure1 is further away from the root promise (the one from new Promise ) than fooBarFailure2 is.不同之处在于fooBarFailure1fooBarFailure2离根承诺(来自new PromisefooBarFailure2 fooBarFailure1 isn't connected to the root promise, it's connected to the one created by .then(fooBarSuccess1) : fooBarFailure1没有连接到根承诺,它连接到由.then(fooBarSuccess1)创建的.then(fooBarSuccess1)

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

In contrast, fooBarSuccess2 and fooBarFailure2 are both attached to the root promise:相比之下, fooBarSuccess2fooBarFailure2附加到根承诺:

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

There's an internal rejection handler in the chain before fooBarFailure1 , but the fooBarFailure2 is hooked directly.fooBarFailure1之前的链中有一个内部拒绝处理程序,但fooBarFailure2是直接挂钩的。 That's what causes the extra async "tick".这就是导致额外异步“滴答”的原因。

Let's look at just the failure example, because it simplifies things:让我们只看失败的例子,因为它简化了事情:

 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.");

The output of that is:其输出是:

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

Here's why:原因如下:

  • Promise.reject() returns a rejected promise in both cases. Promise.reject()在这两种情况下都返回一个被拒绝的承诺。
  • In Promise.reject().then(success).catch(fooBarFailure1);Promise.reject().then(success).catch(fooBarFailure1);
    • .then(success) creates a new promise and hooks up fulfillment and rejection handlers; .then(success)创建一个新的 promise 并连接完成和拒绝处理程序; the rejection handler is internal and just passes on the rejection reason, since no rejection handler was supplied.拒绝处理程序是内部的,只是传递拒绝原因,因为没有提供拒绝处理程序。
    • .catch(fooBarFailure1) hooks up a rejection handler on the promise from then . .catch(fooBarFailure1)then开始在 promise 上连接一个拒绝处理程序。
  • Since the promise is rejected, it uses a microtask to call the rejection handler attached to it.由于承诺被拒绝,它使用一个微任务来调用附加到它的拒绝处理程序。
  • In Promise.reject().then(success, fooBarFailure2);Promise.reject().then(success, fooBarFailure2); :
    • then hooks up both the fulfillment handler ( success ) and the rejection handler ( fooBarFailure2 ) to the promise from Promise.reject() then将履行处理程序 ( success ) 和拒绝处理程序 ( fooBarFailure2 ) 连接到Promise.reject()的承诺
  • Since the promise is rejected, it uses a microtask to call the rejection handler attached to it.由于承诺被拒绝,它使用一个微任务来调用附加到它的拒绝处理程序。
  • "Before setting MicroTask." is logged.已记录。
  • The setTimeout queues its task setTimeout其任务排队
  • The queueMicrotask queues its microtask queueMicrotask其微queueMicrotask排队
  • "After setting MicroTask." is logged.已记录。
  • The task scheduling all this completes, so microtask processing starts至此任务调度完成,微任务处理开始
    • The first microtask is dealing with the rejection from Promise.reject().then(success).catch(fooBarFailure1);第一个微任务是处理来自Promise.reject().then(success).catch(fooBarFailure1); : That rejects the promise created by then , queuing a microtask to call the rejection handler on the promise then returned. :拒绝then创建的承诺,将微任务排队以调用承诺的拒绝处理程序, then返回。
    • The second microtask is dealing with the rejection from Promise.reject().then(success, fooBarFailure2);第二个微任务是处理来自Promise.reject().then(success, fooBarFailure2); : That rejects the promise, calling fooBarFailure2 . :拒绝承诺,调用fooBarFailure2
      • "Failure 2" is logged.记录"Failure 2"
    • The third microtask is the one from queueMicrotask , which runs.第三个微任务来自queueMicrotask ,它运行。
      • "From MicroTask!" is logged.已记录。
    • The microtask call to fooBarFailure1 , scheduled above, runs.上面安排的对fooBarFailure1的微任务调用运行。
      • "Failure 1" is logged.记录"Failure 1"
  • The next task runs, calling the timer callback下一个任务运行,调用定时器回调
    • "This Timeout was set before the MicroTask!" is logged.已记录。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 为什么在事件循环中在宏任务之前执行此微任务? - Why is this microtask executed before macrotask in event loop? 为什么回调队列比微任务队列运行得快? - Why does callback queue runs faster than microtask queue? 当我们的.then 回调被推送到微任务队列时,是否有可能将另一个随机 function 放置在中间的某个位置? - Is there any chance that when our .then callbacks are being pushed onto the microtask queue, another random function is placed somewhere in middle? 为什么这行代码带有'await'触发微任务队列处理? - Why does this line of code with 'await' trigger microtask queue processing? MicroTask Queue 和 MacroTask Queue 的混淆 - Confusion in MicroTask Queue and MacroTask Queue javascript中的微任务队列和回调队列 - microtask queue and callback queue in javascript 宏任务队列和微任务队列优先级 - macrotask queue and microtask queue priority 为什么 setTimeout 为 0ms 的宏任务队列比微任务队列中的任何任务都具有更高的优先级? - Why macrotask queue which has setTimeout with 0ms get higher priority than any task in microtask queue? XHR进度事件微任务队列 - XHR progress event microtask queue 任何证明微任务在渲染之前执行的示例? - Any example proving microtask is executed before rendering?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM