当我调用这个promise时,输出与函数调用的顺序不匹配..then先于.catch,尽管.then的promise 被追捧.原因是什么?

const verifier = (a, b) =>
  new Promise((resolve, reject) => (a > b ? resolve(true) : reject(false)));

verifier(3, 4)
  .then((response) => console.log("response: ", response))
  .catch((error) => console.log("error: ", error));

verifier(5, 4)
  .then((response) => console.log("response: ", response))
  .catch((error) => console.log("error: ", error));

输出

node promises.js
response: true
error: false

推荐答案

这是一个很酷的问题.

当你这么做的时候:

verifier(3,4).then(...)

这将返回一个新的promise ,该promise 需要另一个循环返回到事件循环,然后新拒绝的promise 才能运行后面的.catch()处理程序.这个额外的循环给出了下一个序列:

verifier(5,4).then(...)

有可能在前一行的.catch()之前运行其.then()处理程序,因为在第一行的.catch()处理程序进入队列之前,它已经在队列中,并且项目以FIFO顺序从队列中运行.


请注意,如果您使用.then(f1, f2)表格代替.then().catch()表格,它会在您预期的时间运行,因为没有额外的promise ,因此不涉及额外的勾号:

const verifier = (a, b) =>
  new Promise((resolve, reject) => (a > b ? resolve(true) : reject(false)));

verifier(3, 4)
  .then((response) => console.log("response (3,4): ", response),
        (error) => console.log("error (3,4): ", error)
  );

verifier(5, 4)
  .then((response) => console.log("response (5,4): ", response))
  .catch((error) => console.log("error (5,4): ", error));

请注意,我还标记了所有消息,以便您可以看到它们来自哪个verifier()个呼叫,这使得读取输出更加容易.


ES6 Spec on promise callback ordering and more detailed explanation

ES6规范告诉我们,promise"jobs"(它从.then().catch()调用回调)是根据它们插入作业(job)队列的时间以FIFO顺序运行的.它没有明确命名FIFO,但它指定在队列末尾插入新作业(job),并从队列开头运行作业(job).它实现了FIFO排序.

PerformPromiseThen(执行.then()的回调)将导致EnqueueJob,这就是解析或拒绝处理程序计划实际运行的方式.EnqueueJob指定将挂起的作业(job)添加到作业(job)队列的后面.然后,NextJob操作将该项目从队列前面拉出来.这确保了从Promise作业(job)队列服务作业(job)时的FIFO顺序.

所以,在原始问题的例子中,我们得到了verifier(3,4)promise 和verifier(5,4)promise 的回调,它们按运行顺序插入到作业(job)队列中,因为这两个原始promise 都完成了.然后,当解释器返回到事件循环时,它首先 Select verifier(3,4)个作业(job).这个promise 被拒绝了,而且在verifier(3,4).then(...)年也没有回电.因此,它所做的是拒绝verifier(3,4).then(...)返回的promise ,并将verifier(3,4).then(...).catch(...)处理程序插入到jobQueue中.

然后,它返回到事件循环,它从jobQueue中提取的下一个作业(job)是verifier(5, 4)作业(job).它有一个已解析的promise 和一个解析处理程序,所以它调用该处理程序.这会导致显示response (5,4):输出.

然后,它返回到事件循环,它从jobQueue中提取的下一个作业(job)是它运行的verifier(3,4).then(...).catch(...)个作业(job),这会导致显示error (3,4)个输出.

这是因为第一条链中的.catch()比第二条链中的.then()更深一级,这导致了您报告的订单.而且,这是因为promise 链是通过作业(job)队列以FIFO顺序从一个级别传递到下一个级别的,而不是同步的.


General Recommendation About Relying on This Level of Scheduling Detail

仅供参考,一般来说,我会try 编写不依赖于这种详细计时知识的代码.虽然理解它很奇怪,有时也很有用,但它是脆弱的代码,因为对代码进行简单的看似无害的更改可能会导致相对时间的更改.因此,如果计时在这样的两个链之间是至关重要的,那么我宁愿以一种方式来编写代码,强制按照我想要的方式计时,而不是依赖于这种详细的理解.

Node.js相关问答推荐

postgresql层与应用层的序列化

仅当所需文档是最后一个文档时才更新MongoDB,否则插入

在Android Studio中react 本机构建失败:未正确检测到Node.js版本

如何在Firebase Cloud Function v2计划函数中设置代码中的时区?

PM2 是否需要成为其托管项目的依赖项?

Redis Typescript 删除方法类型转换

DynamoDB 分页数据检索

npm 在 Windows 终端中不工作

错误:无法为 /blog 收集页面数据并且在 object.fetch 处获取失败

找不到react 模块:错误:默认条件应该是最后一个

级联定时器和Jest 的异步功能

MongoDB 根据使用聚合的条件从嵌套数组中删除对象

node/express:使用 Forever 连续运行脚本时设置 NODE_ENV

在 express 中添加故意延迟

使用 MongoDB 更新嵌套数组

已安装全局 NPM 包但未找到命令

通过 POST 请求将数据从 node.js 服务器发送到 node.js 服务器

为什么数组上的js映射会修改原始数组?

nodejs:Ajax 与 Socket.IO,优缺点

将 expressjs 绑定到特定的 IP 地址