一道关于Js执行顺序的题,涉及到promise.then()方法的执行逻辑

130 阅读1分钟

看到一道Js执行顺序的面试题,涉及到promise.then()后面的执行顺序的地方有点疑惑,尤其是then后面的console.log的执行顺序,首先上题目:

console.log("start");

setTimeout(() => {
  console.log("setTimeout1");
}, 0);

(async function foo() {
  console.log("async 1");

  await asyncFunction();

  console.log("async2");

})().then(console.log("foo.then"));

async function asyncFunction() {
  console.log("asyncFunction");

  setTimeout(() => {
    console.log("setTimeout2");
  }, 0);

  new Promise((res) => {
    console.log("promise1");

    res("promise2");
  }).then(console.log);
}

console.log("end");

来源:https://juejin.cn/post/7079681931662589960 第九题

最终的输出顺序是

  1. start
  2. async 1
  3. asyncFunction
  4. promise1
  5. foo.then
  6. end
  7. promise2
  8. async2
  9. setTimeout1
  10. setTimeout2

得到输出结果的时候很疑惑为什么 foo.then会在end之前输出,最开始我认为promise.then()是异步任务且应当是放入微任务队列,而console.log("end")是同步任务应当先执行,这篇文章点醒了我:blog.csdn.net/weixin_4577…

总结一下这里foo.then会在end之前输出的原因:

then方法接受两个参数(onFulfilled, onRejected),官方文档的解释是

If onFulfilled is not a function, it must be ignored.

If onRejected is not a function, it must be ignored.


也就是说如果then接受的参数不是一个函数,那么就会忽略这个then,这也是值穿透的原因

回到本题中来

(async function foo() {
  console.log("async 1");

  await asyncFunction();

  console.log("async2");

})().then(console.log("foo.then"));

这里的then接受的参数是console.log("foo.then")。console.log本身是一个函数,而console.log("foo.then")是执行log函数并得到返回值undefined,因此then()实际接受的参数是undefined,不是函数,就会忽略这个then,所以这里会立即执行console.log("foo.then")并将返回值undefine传递给then方法,而then实际上是被忽略没有执行的。 而后面的一段代码:

  new Promise((res) => {
    console.log("promise1");

    res("promise2");
  }).then(console.log);

这里的then接受的参数是console.log。console.log本身是一个函数,所以这里的then不会被忽略,会被放入微任务队列。 这样就可以理解本题的输出顺序了。