当我调用这个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相关问答推荐

模块';"; node :流程&没有导出的成员';dlopen';

如何在Sequelize with Postgres中将m:n关联表ID从整数迁移到UUID?

如果我加入另一个公会且我的​​机器人已在其中,欢迎消息发送错误

Axios TypeError:将循环 struct 转换为 JSON

SvelteKit应用程序立即退出,没有错误

在mongoose虚拟属性中处理异步操作

TypeScript Eslint警告了一个AWS客户端构造函数(dynamodb),但没有警告另一个(s3)

Node.js中使用ffmpeg如何获取视频截图的位置?

bcrypt.compare 每次都返回 false

在路由上使用中间件时,Nodejs Express 应用程序失败

如何找到特定文档并更新数组中特定键的值?

如何解决 npm install react-select failure with error : An unknown git error occurred, git@github.com :Permission denied (publickey)

带有事件网格的 Azure 函数在没有 ngrok 的情况下在本地运行

在 `DataFrame` 上使用用户定义的可链接函数抽象出 Polars 表达式

使用服务帐户将 Firebase 应用程序部署到 Heroku(使用 dotenv 的环境变量)

Connect/Express 中的session和cookieSession中间件有什么区别?

代理后面的凉亭

yarn 和 npm 的主要区别是什么?

在 Node.js 中获取终端的宽度

deno vs ts-node:有什么区别