这是一个很简单的问题。
执行此操作时:
verifier(3,4).then(...)
它返回一个新的Promise,在新拒绝的Promise可以运行.catch()随后的处理程序之前,它需要另一个循环返回事件循环。这个额外的循环给出了下一个序列:
verifier(5,4).then(...)
有机会.then()在前一行之前运行其处理程序,.catch()因为它已经在队列中.catch(),而第一行中的处理程序已进入队列,并且项目以FIFO顺序从队列中运行。
请注意,如果您使用.then(f1, f2)表单代替,则.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回调排序和更详细的说明
ES6规范告诉我们,诺言“作业”(因为它从a.then()或调用回调.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)作业。它具有一个已解决的Promise和一个已解决的处理程序,因此它将调用该处理程序。这将导致显示response (5,4):输出。
然后,它返回到事件循环,并且从jobQueue中提取的下一个作业是verifier(3,4).then(...).catch(...)运行该作业的作业,这将导致显示error (3,4)输出。
这是因为.catch()第一链中的承诺水平比.then()第二链中的承诺水平更深,这导致了您报告的订购情况。而且,这是因为承诺链是通过作业队列以FIFO顺序从一个级别遍历到下一个级别,而不是同步地遍历。
关于依靠此级别的计划详细信息的一般建议
仅供参考,通常,我尝试编写不依赖于这种详细定时知识水平的代码。尽管它很好奇并且有时有用,但它是易碎的代码,因为对代码的简单看似无害的更改可能会导致相对时间的更改。因此,如果时序在这样的两个链之间至关重要,那么我宁愿以强制时序的方式编写代码,而不希望依赖这种详细的理解。