[英]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.");
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.不同之处在于
fooBarFailure1
比fooBarFailure2
离根承诺(来自new Promise
) fooBarFailure2
。 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:相比之下,
fooBarSuccess2
和fooBarFailure2
都附加到根承诺:
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()
在这两种情况下都返回一个被拒绝的承诺。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 上连接一个拒绝处理程序。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()
的承诺"Before setting MicroTask."
is logged.setTimeout
queues its task setTimeout
其任务排队queueMicrotask
queues its microtask queueMicrotask
其微queueMicrotask
排队"After setting MicroTask."
is logged.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
返回。Promise.reject().then(success, fooBarFailure2);
Promise.reject().then(success, fooBarFailure2);
: That rejects the promise, calling fooBarFailure2
. fooBarFailure2
。
"Failure 2"
is logged."Failure 2"
。queueMicrotask
, which runs.queueMicrotask
,它运行。
"From MicroTask!"
is logged.fooBarFailure1
, scheduled above, runs.fooBarFailure1
的微任务调用运行。
"Failure 1"
is logged."Failure 1"
。"This Timeout was set before the MicroTask!"
is logged.
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.