ES6承诺返回值的执行顺序 [英] ES6 promise execution order for returned values

查看:95
本文介绍了ES6承诺返回值的执行顺序的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

试图了解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"))如上所述.

第一个.then(v => console.log(v))将与v => console.log(v)₁对应的一个Promise反应追加到待处理的Promise p1 的实现反应列表中(仍然

  • p1 现在在满足反应列表中有v => console.log(v)

  • 承诺a.then(v => "B")可以为 p2 .目前,它的工作方式相同.

    • 队列现在为:

      1. 完成反应工作v => Promise.resolve("A")
      2. 完成反应工作v => "B"

    • p1 在其完成反应列表中有v => console.log(v)

    • p2 现在在满足反应列表中包含v => console.log(v)²

    我们已经到达脚本的结尾.

    当对应于v => Promise.resolve("A")的第一份工作出队并被调用时(再次 25.4 .2.1 ),在结果中找到 a then (这是重要的部分),导致另一个作业被排队(

  • 使用 p1 的[[Resolve]]和[[Reject]]
  • 呼叫Promise.resolve("A").then

  • p1 在其完成反应列表中有v => console.log(v)

  • p2 在其完成反应列表中包含v => console.log(v)²
  • 下一个作业出队并调用.结果中没有找到可调用的then ,因此 p2 立即得到满足(

  • 致电(通过 25.4.2.1 )v => console.log(v)²
  • p1 在其完成反应列表中有v => console.log(v)

  • 由于Promise.resolve("A").then会重新开始整个then序列,因此我将在这里停止这种解释.不过,您可以看到它的运行方向:作业队列是一个 queue 队列,一个将产生输出的函数位于队列中,而另一个函数尚未添加.队列中的那个将首先运行.

    正确的输出是B,然后是A.

    那么,既然如此,为什么答案在单独页面的Chrome中是错误的呢?这不是某些Stack Overflow代码片段填充程序;您可以单独或在节点中使用一些HTML来重现它.我的猜测是,这是一项违反规格的优化.

     '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));
    

    ¹对于后代: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

    let a = Promise.resolve();
    a.then(v => Promise.resolve("A")).then(v => console.log(v));
    a.then(v => "B").then(v => console.log(v));

    Output when run directly in Chrome (v 61) console:

    B
    A

    However, when clicking the Run code snippet button, I will get the order A B instead.

    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?

    解决方案

    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¹).

    Let’s name the promise 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.

    The first .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).

    • The queue is now:

      1. fulfill reaction job v => Promise.resolve("A")

    • p1 now has v => console.log(v)₁ in its list of fulfill reactions

    The promise a.then(v => "B") can be p2. It works in the same way for now.

    • The queue is now:

      1. fulfill reaction job v => Promise.resolve("A")
      2. fulfill reaction job v => "B"

    • p1 has v => console.log(v)₁ in its list of fulfill reactions

    • p2 now has v => console.log(v)₂ in its list of fulfill reactions

    We have reached the end of the script.

    When the first job, corresponding to v => 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.

    • The queue is now:

      1. fulfill reaction job v => "B"
      2. call Promise.resolve("A").then with p1’s [[Resolve]] and [[Reject]]

    • p1 has v => console.log(v)₁ in its list of fulfill reactions

    • p2 has v => console.log(v)₂ in its list of fulfill reactions

    The next job is dequeued and called. A callable then 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.

    • The queue is now as follows:

      1. call Promise.resolve("A").then with p1’s [[Resolve]] and [[Reject]]
      2. call (via 25.4.2.1) v => console.log(v)

    • p1 has v => console.log(v)₁ in its list of fulfill reactions

    I’m going to stop this level of explanation here, as Promise.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.

    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.

    '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));

    Alternate definitions of 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));
    

    ¹ For posterity: it’s a resolved promise in Chrome 61.0.3163.100.
    ² 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.

    这篇关于ES6承诺返回值的执行顺序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆