ES6承诺返回值的执行顺序 [英] ES6 promise execution order for returned values
问题描述
试图了解ES6 Promise的执行顺序,我注意到链式处理程序的执行顺序受前一个处理程序返回值或Promise的影响.
示例
let a = Promise.resolve();
a.then(v => Promise.resolve("A")).then(v => console.log(v));
a.then(v => "B").then(v => console.log(v));
直接在Chrome(v 61)控制台中运行时的输出:
B
A
但是,当单击Run code snippet
按钮时,我将获得订单A
B
.
是上面示例中ES6中定义的执行顺序,还是取决于实现?
如果已定义,正确的输出应该是什么?
Promise.resolve
被指定为返回已解决的承诺(令人振奋,对吗?25.4.4.5、25.4.1.5、25.4.1.3.). a.then()
因此立即入队( 25.4.5.3.1 ,步骤8 ) 每一次. .then()
永远不会返回符合此规范的承诺(出于有趣的原因,请在您的Chrome控制台中尝试Promise.resolve().then()
.)
让我们将承诺命名为a.then(v => Promise.resolve("A"))
,并将其某些关联的规范状态命名为 p1 ².此.then()
排队要调用的工作( 25.4.2.1 )a.then(v => Promise.resolve("A"))
如上所述.
第一个
p1 现在在满足反应列表中有 承诺 队列现在为: p1 在其完成反应列表中有 我们已经到达脚本的结尾. 当对应于
p1 在其完成反应列表中有 下一个作业出队并调用.结果中没有找到可调用的
p1 在其完成反应列表中有 由于 正确的输出是B,然后是A. 那么,既然如此,为什么答案在单独页面的Chrome中是错误的呢?这不是某些Stack Overflow代码片段填充程序;您可以单独或在节点中使用一些HTML来重现它.我的猜测是,这是一项违反规格的优化.
用这个有趣的 ¹对于后代:Chrome 61.0.3163.100中已解决的诺言. Trying to understand the order of execution of ES6 promises, I noticed the order of execution of chained handlers is affected by whether the previous handler returned a value or a promise. Example
Output when run directly in Chrome (v 61) console: B However, when clicking the Is the execution order defined in ES6 for the above example, or is it up to the implementation? If it is defined, what should be the correct output? Let’s name the promise The first The queue is now: p1 now has The promise The queue is now: p1 has We have reached the end of the script. When the first job, corresponding to The queue is now: p1 has The next job is dequeued and called. A callable The queue is now as follows: p1 has I’m going to stop this level of explanation here, as The correct output is B followed by A. So, with that out of the way, why is the answer wrong in Chrome in a page by itself? It’s not some Stack Overflow snippets shim; you can reproduce it with a bit of HTML on its own or in Node. My guess is that it’s a spec-breaking optimization.
Alternate definitions of ¹ For posterity: it’s a resolved promise in Chrome 61.0.3163.100. 这篇关于ES6承诺返回值的执行顺序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!.then(v => console.log(v))
将与v => console.log(v)
₁对应的一个Promise反应追加到待处理的Promise p1 的实现反应列表中(仍然
v => console.log(v)
₁a.then(v => "B")
可以为 p2 .目前,它的工作方式相同.
v => Promise.resolve("A")
v => "B"
v => console.log(v)
₁v => console.log(v)
²v => Promise.resolve("A")
的第一份工作出队并被调用时(再次 25.4 .2.1 ),在结果中找到 a then
(这是重要的部分),导致另一个作业被排队(
Promise.resolve("A").then
v => console.log(v)
₁v => console.log(v)
²then
,因此 p2 立即得到满足(
v => console.log(v)
²v => console.log(v)
₁Promise.resolve("A").then
会重新开始整个then
序列,因此我将在这里停止这种解释.不过,您可以看到它的运行方向:作业队列是一个 queue 队列,一个将产生输出的函数位于队列中,而另一个函数尚未添加.队列中的那个将首先运行.
'use strict';
class Foo extends Promise {}
let a = Promise.resolve();
a.then(v => Foo.resolve("A")).then(v => console.log(v));
a.then(v => "B").then(v => console.log(v));
node --allow_natives_syntax
脚本替换thenable
的定义!'use strict';
const thenable = p => ({ then: p.then.bind(p) });
//const thenable = p => p;
let a = Promise.resolve();
a.then(v => {
%EnqueueMicrotask(() => {
%EnqueueMicrotask(() => {
console.log("A should not have been logged yet");
});
});
return thenable(Promise.resolve("A"));
}).then(v => console.log(v));
a.then(v => "B").then(v => console.log(v));
²比规范没有那么具体,但这是试图描述规范而不是规范的答案.运气还不错. let a = Promise.resolve();
a.then(v => Promise.resolve("A")).then(v => console.log(v));
a.then(v => "B").then(v => console.log(v));
ARun code snippet
button, I will get the order A
B
instead.Promise.resolve
is specified to return a resolved promise (mind-blowing, right? 25.4.4.5, 25.4.1.5, 25.4.1.3.). a.then()
therefore enqueues a job immediately (25.4.5.3.1, step 8) each time. .then()
never returns a fulfilled promise according to this spec (for something interesting, try Promise.resolve().then()
in your Chrome console¹).a.then(v => Promise.resolve("A"))
and some of its associated spec state p1². This .then()
enqueues a job to call (25.4.2.1) a.then(v => Promise.resolve("A"))
as stated above..then(v => console.log(v))
appends a promise reaction corresponding to v => console.log(v)
₁ to the list of fulfill reactions of the pending promise p1 (still 25.4.5.3.1).
v => Promise.resolve("A")
v => console.log(v)
₁ in its list of fulfill reactionsa.then(v => "B")
can be p2. It works in the same way for now.
v => Promise.resolve("A")
v => "B"
v => console.log(v)
₁ in its list of fulfill reactionsv => console.log(v)
₂ in its list of fulfill reactionsv => Promise.resolve("A")
, is dequeued and called (again 25.4.2.1), a then
is found on the result (this is the important part), causing another job to be enqueued (25.4.1.3.2, step 12) regardless of the promise state of that result.
v => "B"
Promise.resolve("A").then
with p1’s [[Resolve]] and [[Reject]]v => console.log(v)
₁ in its list of fulfill reactionsv => console.log(v)
₂ in its list of fulfill reactionsthen
is not found on the result, so p2 is fulfilled immediately (25.4.1.3.2 again, step 11a) and enqueues a job for each of p2’s fulfill reactions.
Promise.resolve("A").then
with p1’s [[Resolve]] and [[Reject]]v => console.log(v)
₂v => console.log(v)
₁ in its list of fulfill reactionsPromise.resolve("A").then
starts the entire then
sequence again. You can see where this is going, though: the job queue is a queue, and one function that’s going to produce output is in the queue and the other hasn’t yet been added. The one that’s in the queue is going to run first.'use strict';
class Foo extends Promise {}
let a = Promise.resolve();
a.then(v => Foo.resolve("A")).then(v => console.log(v));
a.then(v => "B").then(v => console.log(v));
thenable
with this fun node --allow_natives_syntax
script!'use strict';
const thenable = p => ({ then: p.then.bind(p) });
//const thenable = p => p;
let a = Promise.resolve();
a.then(v => {
%EnqueueMicrotask(() => {
%EnqueueMicrotask(() => {
console.log("A should not have been logged yet");
});
});
return thenable(Promise.resolve("A"));
}).then(v => console.log(v));
a.then(v => "B").then(v => console.log(v));
² That’s less specific than the spec, but this is an answer trying to describe the spec and not a spec. With any luck, it’s even right, too.