乐趣区

关于前端:你可能不了解的-Promise-微任务类型

一道示例题引发的常识盲区

// promise1
new Promise(resolve => {
  resolve(
    new Promise(resolve => {resolve(1);
    })
  );
}).then(res => {console.log('1');
});

new Promise(resolve => {resolve(2);
})
  .then(() => {console.log('2');
  })
  .then(() => {console.log('3');
  })
  .then(() => {console.log('4');
  });

// 输入程序:// 2
// 3
// 1
// 4

先来看一道示例题。依照以往的了解,我认为输入程序是 2 1 3 4。而后通过调试发现 promise1 在初始化后状态仍然是 pending,感觉本人在了解 Promise 微工作方面还是存在有余。钻研了下 ECMA 标准,终于把这个问题搞清楚了。

微工作的类型

ECMA 标准中把 Promise 微工作分成了两种类型,上面联合标准来别离看下这两种微工作的产生机会和执行内容。

NewPromiseReactionJob

当 Promise 决定后执行 then() 中注册的回调时,或当 then() 注册时 Promise 已决定,会产生这种微工作。为简化阐明,咱们先关注这个场景:以后 Promise 曾经决定,接着调用了 then(),比方这样:

Promise.resolve(1).then(res => console.log(res));

res => console.log(res) 就运行在这种微工作中。来看标准对于 Promise.prototype.then 的形容:

因为 Promise 曾经是 fulfilled 状态,咱们接着看 PerformPromiseThen 中对于 fulfilled 状态的操作:

这里就是创立了一个 NewPromiseReactionJob 微工作,并退出到了微工作队列中。咱们再看看 NewPromiseReactionJob 外面是怎么执行的:

该微工作次要蕴含两个内容:

  1. 执行 handler,handler 就是 then() 中注册的回调,失去返回后果。
  2. 对 then() 中产生的新 Promise 执行 resolve( 返回后果) 或 reject(返回后果)。

NewPromiseResolveThenableJob

下面那种微工作根本是大家熟知的状况,这个微工作类型就是示例题中提到的盲区了。首先留神到 resolve 函数的形容:

如果一个对象的 then 属性能够被调用(是一个函数),那么这个对象就是 thenable 对象。调用 resolve() 传递的参数值如果是一个 thenable 对象,就会产生 NewPromiseResolveThenableJob 这种微工作了。接下来看看这个微工作的内容:

大略意思就是这种微工作产生了如下的代码:

// resovle 和 reject 是调用 resolve(thenable) 时那个 Promise 上的。thenable.then(resolve, reject); 

那么联合第一种微工作,如果 thenable 对象是 Promise,则这个微工作执行后又会产生第一个微工作。为什么要这样做呢?标准上有一段解释:

This Job uses the supplied thenable and its then method to resolve the given promise. This process must take place as a Job to ensure that the evaluation of the then method occurs after evaluation of any surrounding code has completed.

间接翻译的话大略就是说要等四周的同步代码执行完后才会执行这个。对于这个设计用意,我的了解是思考到 thenable 对象的不肯定是 Promise 实例,也可能是用户创立的任何对象;如果这个对象的 then 是同步办法,那么这样做就能够保障 then 的执行程序也是在微工作中。

示例题剖析

咱们再来剖析一下示例题:

// promise1
new Promise(resolve => {
  resolve(
    // promise2
    new Promise(resolve => {resolve(1);
    })
  );
}).then(res => {console.log('1');
});

// promise3
new Promise(resolve => {resolve(2);
})
  .then(() => {console.log('2');
  })
  .then(() => {console.log('3');
  })
  .then(() => {console.log('4');
  });

代码执行后,咱们用伪代码来示意下微工作队列的内容:

const microTasks = [function job1() {promise2.then(promise1.[[Resolve]], promise1.[[Reject]]);
  },
  function job2() {const handler = () => {console.log('2');
    };
    
    // 决定 then() 返回的新 Promise。resolve(handler(2));
  }
];

接着开始执行微工作队列。job 1 执行后,产生了新的微工作 job 3:

const microTasks = [function job2() {const handler = () => {console.log('2');
    };
    resolve(handler(2));
  },
  function job3() {const handler = promise1.[[Resolve]];
    resolve(handler(1));
  }
];

job 2 执行后,输入了 2,并且产生新的微工作 job 4:

const microTasks = [function job3() {const handler = promise1.[[Resolve]];
    resolve(handler(1));
  },
  function job4() {const handler = () => {console.log('3');
    };
    resolve(handler(undefined));
  }
];

留神到 job 3 的内容是会让 promise1 决定,那么就会执行 promise1 的 then 回调,则会再产生一个微工作 job 5;并且 job 4 执行完后输入变为 2 3,并让 then() 产生的新 Promise 决定,也会再产生下一个的微工作 job 6:

const microTasks = [
  // job 5 由 job 3 产生。function job5() {const handler = () => {console.log('1');
    };
    resolve(handler(1));
  },
  function job6() {const handler = () => {console.log('4');
    };
    resolve(handler(undefined));
  }
];

那么最初的输入后果就是 2 3 1 4 啦,大家能够把以上分析方法放在其余的题目中验证康康是不是对的。

我的 JS 博客:小声比比 JavaScript

参考资料

  • ECMAScript® 2023 Language Specification – Promise Objects
退出移动版