为了账号安全,请及时绑定邮箱和手机立即绑定

Javascript 承诺好奇心

Javascript 承诺好奇心

胡子哥哥 2022-12-09 15:18:03
当我调用这个 promise 时,输出与函数调用的顺序不匹配。The.then出现在 之前.catch,即使 promise with.then被调用之后。这是什么原因?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.jsresponse: trueerror: false
查看完整描述

2 回答

?
扬帆大鱼

TA贡献1799条经验 获得超9个赞

这是一个很酷的问题,需要深入探究。


当你这样做时:


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

返回一个新的承诺,在新拒绝的承诺可以运行后面的处理程序之前,需要另一个循环回到事件循环.catch()。这个额外的循环给出了下一个序列:


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

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


请注意,如果您使用.then(f1, f2)form 代替.then().catch(),它会在您期望的时候运行,因为没有额外的承诺,因此不涉及额外的滴答:


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 回调排序和更详细的解释

.then()ES6 规范告诉我们,promise“作业”(因为它调用 a或 的回调.catch())根据它们被插入作业队列的时间以 FIFO 顺序运行。它没有具体命名 FIFO,但它指定新作业插入队列末尾,作业从队列开头运行。这实现了 FIFO 排序。

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

因此,在原始问题的示例中,我们获得了verifier(3,4)promise 的回调和按verifier(5,4)运行顺序插入作业队列的 promise,因为这两个原始 promise 都已完成。然后,当解释器返回到事件循环时,它首先开始执行verifier(3,4)任务。该承诺被拒绝,并且在verifier(3,4).then(...). 因此,它所做的是拒绝verifier(3,4).then(...)返回的承诺,并导致将verifier(3,4).then(...).catch(...)处理程序插入到 jobQueue 中。

然后,它返回到事件循环,它从 jobQueue 中提取的下一个作业就是作业verifier(5, 4)。它有一个已解决的承诺和一个解决处理程序,因此它调用该处理程序。这会导致response (5,4):显示输出。

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

这是因为.catch()第一个链中的承诺级别在其链中比.then()第二个链中的承诺级别更深,这导致了您报告的排序。而且,这是因为 promise 链是通过作业队列以 FIFO 顺序从一个级别遍历到下一个级别,而不是同步的。


关于依赖此级别的调度详细信息的一般建议

仅供参考,一般来说,我尝试编写不依赖于这种详细时序知识的代码。虽然它很好奇并且有时对理解很有用,但它是脆弱的代码,因为对代码进行看似无害的简单更改可能会导致相对时序发生变化。因此,如果像这样的两条链之间的时序至关重要,那么我宁愿以一种强制按照我想要的方式进行时序的方式编写代码,而不是依赖于这种详细的理解水平。


查看完整回答
反对 回复 2022-12-09
?
哆啦的时光机

TA贡献1779条经验 获得超6个赞

Promise.resolve()

  .then(() => console.log('a1'))

  .then(() => console.log('a2'))

  .then(() => console.log('a3'))

Promise.resolve()

  .then(() => console.log('b1'))

  .then(() => console.log('b2'))

  .then(() => console.log('b3'))

由于相同的原因,您将看到 a1、b1、a2、b2、a3、b3 而不是输出 a1、a2、a3、b1、b2、b3 - 每个 then 都会返回一个 promise 并且它会进入事件循环的末尾队列。所以我们可以看到这个“无极跑”。当有一些嵌套的承诺时也是如此。



查看完整回答
反对 回复 2022-12-09
  • 2 回答
  • 0 关注
  • 85 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信