为什么用 p.then(resolve) 比用resolve(p) 更早地解决新的 Promise?

2024-03-15

Code#1 和 Code#2 之间的区别是:Code#1 使用resolve(p)和代码#2 使用p.then(()=>resolve())。我希望输出的序列是不变的,但它们会生成不同的序列。我不明白为什么。

代码#1:resolve(p)

const p = Promise.resolve();

new Promise((resolve) => {
    resolve(p);    // <---
}).then(() => {
    console.log('after:await');
});

p.then(() => console.log('tick:a'))
    .then(() => console.log('tick:b'))
    .then(() => console.log('tick:c'));

Output:

tick:a
tick:b
after:await
tick:c

代码#2:p.then(()=>resolve())

const p = Promise.resolve();

new Promise((resolve) => {
    p.then(()=>resolve());    // <---
}).then(() => {
    console.log('after:await');
});

p.then(() => console.log('tick:a'))
    .then(() => console.log('tick:b'))
    .then(() => console.log('tick:c'));

Output:

tick:a
after:await
tick:b
tick:c

为什么输出的顺序不同?


这其实是一个非常有趣的问题,因为承诺/A+ 规格 https://promisesaplus.com/将允许第一个代码版本产生与第二个代码版本相同的输出。

有人可能会驳回这个问题,说 Promise 的实现没有说明如何实现resolve(p)将被实施。在查看 Promise/A+ 规范时,这是一个真实的陈述,引用其前言:

核心 Promises/A+ 规范不涉及如何创建、履行或拒绝 Promise,...

但是 Promise 的 EcmaScript 规范(第25.4 https://www.ecma-international.org/ecma-262/6.0/#sec-promise-objects)比 Promise/A+ 规范更加详细,并要求将“作业”添加到相关作业队列的后面——对于 Promise 结算来说,这是乔布斯 queue (25.4.1.3.2 https://www.ecma-international.org/ecma-262/6.0/#sec-promise-objects and 8.4 https://www.ecma-international.org/ecma-262/6.0/#sec-jobs-and-job-queues):这决定了特定的顺序:

所需的作业队列

[...]
乔布斯:对 Promise 解决的响应的作业

[...]

The 待处理的作业来自单个作业队列的记录始终按 FIFO 顺序启动

它还定义了resolve(p)- 什么时候p是一个thenable——意志first将一个作业放入队列中,该作业将执行必要的内部调用p.then方法。这是not立即完成。引用 EcmaScript 规范中的注释:25.4.2.2 https://www.ecma-international.org/ecma-262/6.0/#sec-promiseresolvethenablejob:

此过程必须作为一项工作进行,以确保对then方法在任何周围代码的评估完成后发生。

以下代码片段中的输出顺序说明了该语句:

const p1 = Promise.resolve();

// Wrap the `p1.then` method, so we can log something to the console:
const origThen = p1.then;
p1.then = function(...args) {
    console.log("The p1.then method is called asynchronously when triggered by resolve(p1)");
    origThen.call(this, ...args);
};
const p2 = new Promise(resolve => {
    resolve(p1);
    console.log("Code that follows is executed synchronously, before p1.then is");
});

当我们使用p1.then(resolve)方法调用而不是resolve(p1),我们得到相反的顺序:

const p1 = Promise.resolve();

// Wrap the `p1.then` method, so we can log something to the console:
const origThen = p1.then;
p1.then = function(...args) {
    console.log("The p1.then method is called synchronously now");
    origThen.call(this, ...args);
};
const p2 = new Promise(resolve => {
    p1.then(resolve);
    console.log("Code that follows is executed synchronously, after p1.then is");
});

你的代码

上面确实解释了您得到的不同输出顺序。以下是第一个代码版本如何对操作进行排序。首先让我稍微重写一下,以便大多数涉及的承诺都有一个名称:

const p1 = Promise.resolve();
const p2 = new Promise((resolve) => resolve(p1));
const p3 = p2.then(() => console.log('after:await'));
const p4 = p1.then(() => console.log('tick:a'));
const p5 = p4.then(() => console.log('tick:b'))
const p6 = p5.then(() => console.log('tick:c'));

现在,在主同步代码执行完成之后,仅p1具有已解决状态,并且作业队列(微任务队列)上存在两个作业,其中一个是由于resolve(p1)第二个是因为p1.then:

  1. 根据25.4.2.2 https://www.ecma-international.org/ecma-262/6.0/#sec-promiseresolvethenablejob, the then的方法p1称为传递内部[[resolve]]相关函数p2. The p1.then内部人士知道p1已解决并将另一个作业放入队列中以实际解决p2!

  2. 带有“tick:a”的回调被执行,promise p4 被标记为已完成,在作业队列中添加一个新作业。 现在队列中有 2 个新作业,按顺序处理:

  3. 执行步骤 1 中的作业:p2 现在已解析。这意味着一个新作业排队等待实际调用相应的then回调

  4. 执行步骤 2 中的作业:执行带有“tick:b”的回调

只有稍后,步骤 3 中添加的作业才会被执行,这将通过“after:await”调用回调。

所以,总而言之。在 EcmaScript 中resolve(p), where p是一个 thenable 涉及一个异步作业,它本身会触发另一个异步作业来通知完成。

The then区分第二个代码版本的回调,只需要one异步作业被调用,因此它发生在“tick:b”输出之前。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么用 p.then(resolve) 比用resolve(p) 更早地解决新的 Promise? 的相关文章

随机推荐

  • 如何更快地加载JQuery?

    我有aspx 其中有jquery 由于加载 jquery 的延迟 我面临一些样式问题 请谁能告诉我如何快速加载jquery 我今天读了 Stackoverflow 上 Sam Saffron 撰写的关于此主题的博客文章 我还没有尝试过作者的
  • 上传到 iTunes Store 时出错

    我们确实需要一些帮助 在过去的两个月里 我们一直在与所有 Apple Mumbo Jumbo 进行斗争 但似乎无法在 APPStore 上获取我们的应用程序 现在我的问题是在验证存档编译并共享它之后 在提交过程中我得到 上传到 iTunes
  • 干净的条件格式 (Excel VBA)

    如果这个问题已经得到解答 但我无法找到它 我深表歉意 这就是我想要的 我们都知道删除范围 行和列会分割条件格式并使其变得丑陋 我想创建一个个人宏 1 Searches through all existing Conditional For
  • Errno::EACCES: 权限被拒绝@ rb_sysopen

    当我尝试运行 gem install json v 1 8 3 时 错误 执行 gem 时 Errno EACCES 权限被拒绝 rb sysopen home ulap10 gem gems json 1 8 3 tests test j
  • 我如何告诉编译器 MyCustomType 与 SomeOtherType 是 equal_comparable_with SomeOtherType ?

    假设我有一个MyCustomType与SomeOtherType struct SomeOtherType int value constexpr bool operator const SomeOtherType rhs const de
  • 使用 LINQ 查找对称差异

    我有两个收藏a and b 我想计算其中的一组项目a or b 但不能同时存在 逻辑异或 有了 LINQ 我可以想出这个 IEnumerable
  • R 中逻辑回归的致死剂量 (LD) 置信区间

    我想找到致命剂量 LD50 其置信区间为R Minitab SPSS SAS 等其他软件提供了此类置信区间的三种不同版本 我在任何包中都找不到这样的间隔R 我也用过findFn函数来自sos包裹 我怎样才能找到这样的间隔 我根据 Delta
  • 在 Linux 中使用 TCP 时,listen 的积压数量是否包括 SYN 接收的连接数?

    我读了一些帖子并检查了 Linux 内核代码 例如inet listen gt inet csk listen start https elixir bootlin com linux v4 3 6 source net ipv4 inet
  • 如果我更改扬声器配置,ffmpeg avcodec_open2 返回 -22

    我最近总是遇到一个奇怪的问题 根据我在 Windows stereo quad 5 1 中设置音频配置的方式 对 avcodec open2 的 ffmpeg 调用失败并出现错误 22 或正常工作 由于找不到太多有关该错误的信息 我想我应该
  • Ghostscript 顽固地拒绝嵌入字体

    我有一个从 pdflatex 创建的文档 嵌入 R pdf 图表 我现在正尝试将其发送到要求嵌入所有字体的打印机 lulu 我认为 没问题 gs dNOPAUSE dBATCH dNOPLATFONTS sDEVICE pdfwrite d
  • 如何:点击时将视图置于前面;设置最大/最小捏合手势比例;将屏幕限制设置为平移手势

    注意 我发现的所有东西都太旧了 或者与我实际需要的东西相差太大 而且我几乎尝试了我发现的所有东西 但没有一个能以正确的方式工作 我创建了 3 个 UIView 我可以拖动它们并缩放它们 现在 我需要 将其中一些放在前面 我想为每个添加一个
  • 使用owin中间件替换响应体

    有没有办法使用 OWIN 中间件覆盖响应内容 我的自定义错误类 public class Error public string error get set public string description get set public
  • RxJs - forkJoin 与空数组

    我目前正在使用forkJoin等待数组Observable s 之前完成pipe 英 和tap ping 我注意到如果数组为空 则不会发出任何信号 我什至不能tap 我该如何解决这种问题 我应该检查数组是否为空吗 myFirstFuncti
  • AspNetCore 2.0 声明始终为空

    我正在努力将 DotNet 4 5 MVC WebAPI 应用程序转换为 AspNetCore 2 0 但在让 Cookie 身份验证再次工作时遇到一些问题 当我设置 cookie 并尝试访问安全方法时 我无法到达那里 当我进入匿名方法并检
  • 空比较

    有一个查询 UPDATE MyTable SET nvarchar1 blahblah WHERE Id 096fe792 7313 416f b3c8 327f46be73b6 AND nvarchar1 lt gt blablah 当
  • 使用 Delphi 检测互联网连接激活

    我使用 3G 无线卡已经有一段时间了 每次连接时 我的防病毒软件都会启动更新 我想知道我可以使用哪些 Win32 API 函数集来获取通知或查询即将出现的 Internet 连接事件 Delphi 已经有一组移植的标头了吗 我参与了一个项目
  • 如何将无边框表单对齐到屏幕边缘?

    当我将窗体的边框样式设置为无时 我无法再将窗口捕捉到屏幕边缘 我怎样才能两者兼得 Aero Snap 需要一个有边框的窗口 没有后门 您可以使用以下代码来模拟捕捉这个帖子 https stackoverflow com a 591734 1
  • ruby/ruby on Rails 内存泄漏检测

    我使用 ruby on Rails 编写了一个小型 Web 应用程序 其主要目的是上传 存储和显示 xml 文件最多可达几 MB 文件的结果 运行大约 2 个月后 我注意到杂种进程使用了 大约 4GB 内存 我做了一些关于调试 ruby 内
  • 单页网站上复杂的活动状态导航

    HTML div class logo ribbon a href top a div
  • 为什么用 p.then(resolve) 比用resolve(p) 更早地解决新的 Promise?

    Code 1 和 Code 2 之间的区别是 Code 1 使用resolve p 和代码 2 使用p then gt resolve 我希望输出的序列是不变的 但它们会生成不同的序列 我不明白为什么 代码 1 resolve p cons