我最近在Stack Overflow上遇到了this question个问题,询问THEN方法在JavaScript中到底是如何工作的.回复人Trin Cot发表了以下 comments :

主机将判断哪些作业(job)队列具有条目,优先考虑具有高优先级的作业(job)队列.Promise作业(job)队列具有非常高的优先级,通常高于处理用户交互或其他外部事件的事件队列.因此,在上述步骤4中放置在队列中的作业(job)将从Promise作业(job)队列中取出.该作业(job)将按顺序调用已注册为myPromise对象上的回调函数的回调函数(如在步骤7中注册的回调函数).

如果我正确理解了他的解释,那么每当调用Resolve函数时,JavaScript都会在作业(job)队列中调度一个微任务(或作业(job)),当调用堆栈为空时,该作业(job)队列将执行传递给THEN的所有回调,包括链接.例如:

Promise.resolve("Hi")
    .then(() => {
        console.log("Hi");
    })
    .then(() => {
        console.log("World");
    });

在这种情况下,promise 被立即解析,这意味着微任务将被立即调度.当THEN回调完成执行时,JS引擎将判断队列中的任何微任务.因为promise 是立即解析的,所以它将执行微任务,而微任务又执行传递给THEN方法的所有处理程序.因此,代码将输出"Hi"和"World".

但是,为什么这段代码输出的是"1 3 2 4"而不是"1 2 3 4"?

const p1 = Promise.resolve();
const p2 = Promise.resolve();
p1.then(() => {
    console.log(1);
}).then(() => {
    console.log(2);
});
p2.then(() => {
    console.log(3);
}).then(() => {
    console.log(4);
});

我认为代码应该输出"%1 2%3%4",而不是"%1%3%2%4".这是因为,当p1和p2解析时,两个微任务被排队.当随后的回调完成执行时,它们会将这些回调添加到其内部列表中.一旦调用堆栈为空,JS引擎就从队列中 Select 最旧的任务并执行它.该任务应该执行传递给该特定Promise实例的所有处理程序.然而,这似乎并没有像预期的那样发生.谁能给我解释一下这种行为背后的原因?

感谢您抽出时间,祝您度过愉快的一天!

推荐答案

当p1和p2解析时,两个微任务被排队.

这是一个误会.微任务(Promise作业(job))在以下情况下入队:

  • 对已解析的promise 执行then方法
  • Promise解析,在它解析之前,已经有一个then方法在它上面执行:对于每个这样的then回调,创建一个separate作业(job).

因此,在执行完前两条语句后,Promise作业(job)队列中还没有添加任何内容.

认识到这一点:

  • then方法执行时,当promise 已被解析时,其回调被放入promise 作业(job)队列only.否则,它将是registered,等待promise 来解决.
  • 当执行then方法时,它返回一个always挂起的promise ,即使对已解析的promise 调用它也是如此.

它可能有助于识别由then()个变量调用返回的promise ,还可以命名回调函数.

这将使您的脚本如下所示:

const p1 = Promise.resolve();
const p2 = Promise.resolve();
const q1 = p1.then(function p1_then() { console.log(1) });
const r1 = q1.then(function q1_then() { console.log(2) });
const q2 = p2.then(function p2_then() { console.log(3) });
const r2 = q2.then(function q2_then() { console.log(4) });

以下是该脚本中所发生情况的简化视图:

  • 上下文标识执行当前操作的位置
  • 该操作是执行的当前语句
  • 每个命名的promise 都有自己的一列:?表示它处于待定状态;F表示它已实现.
  • 最后一列列出了Promise作业(job)队列中的当前条目
Context Action p1 p2 q1 r1 q2 r2 Promise Job Queue
Script p1 = Promise.resolve() F - - - - - -
p2 = Promise.resolve() F F - - - - -
q1 = p1.then(p1_then) F F ? - - - p1_then
r1 = q1.then(q1_then) F F ? ? - - p1_then
q2 = p2.then(p2_then) F F ? ? ? - p1_then,p2_then
r2 = q2.then(q2_then) F F ? ? ? ? p1_then,p2_then
Host get job p1_then F F ? ? ? ? p2_then
p1_then console.log(1) F F ? ? ? ? p2_then
p1_then (return) F F F ? ? ? p2_then,q1_then
Host get job p2_then F F F ? ? ? q1_then
p2_then console.log(3) F F F ? ? ? q1_then
p2_then (return) F F F ? F ? q1_then,q2_then
Host get job q1_then F F F ? F ? q2_then
q1_then console.log(2) F F F ? F ? q2_then
q1_then (return) F F F F F ? q2_then
Host get job q2_then F F F F F ? -
q2_then console.log(4) F F F F F ? -
q2_then (return) F F F F F F -
Host nothing to do F F F F F F -

Javascript相关问答推荐

React native在不首次加载时渲染的挂钩比上一次渲染期间渲染的挂钩更多

jest使用useLocate测试自定义挂钩

v-textfield的规则找不到数据中声明的元素

Angular 拦截器错误处理删除方法问题

Fastify错误:CORS策略已阻止从起源api-dev.example.com上的MLhttp请求

我不明白这个react 组件有什么问题

从外部访问组件变量-Vueejs

通过实现regex逻辑自定义数据表搜索

成帧器运动中的运动组件为何以收件箱开始?

如何访问Json返回的ASP.NET Core 6中的导航图像属性

如何使用侧边滚动按钮具体滚动每4个格?

硬币兑换运行超时

我不知道为什么setwritten包装promise 不能像我预期的那样工作

为什么我的列表直到下一次提交才更新值/onChange

TypeScript索引签名模板限制

显示图—如何在图例项上添加删除线效果?

如何在JavaScript文件中使用Json文件

为什么我的导航条打开状态没有在窗口addeventlistener(Reaction Js)中更新?

这个值总是返回未定义的-Reaction

有没有一种直接的方法可以深度嵌套在一个JavaScript对象中?