JS Guide JS Guide
首页
  • JS 部分
  • HTML 部分
  • CSS 部分
  • Vue
  • React & Angular
  • 数据结构、算法、OS
  • 计网、浏览器
  • 杂项
笔试
面试
资源
资讯
  • 关于本站
  • 更新历史
  • 贡献指南
  • 文档规范
  • 常见问题
GitHub (opens new window)
首页
  • JS 部分
  • HTML 部分
  • CSS 部分
  • Vue
  • React & Angular
  • 数据结构、算法、OS
  • 计网、浏览器
  • 杂项
笔试
面试
资源
资讯
  • 关于本站
  • 更新历史
  • 贡献指南
  • 文档规范
  • 常见问题
GitHub (opens new window)
  • 面试相关说明
  • 解释代码题

    • 隐式转换问题
    • this 指向问题
    • Promise 与异步执行问题
      • 1. 字节:解释以下异步代码的输出顺序
      • 2. 解释以下异步代码的输出顺序
      • 3. 解释以下异步代码的输出顺序
  • 手撕代码题

  • 个人相关

  • 面试相关
  • 解释代码题
卡洛
2022-11-02
目录

Promise 与异步执行问题

参考知识点:

  • JS 知识点 - Promise 与异步
  • JS 知识点 - 事件循环

# 1. 字节:解释以下异步代码的输出顺序

#

async function async1() {
  console.log('async1 start')
  await async2()
  console.log('async1 end')
}
async function async2() {
  console.log('async2')
}
console.log('script start')
setTimeout(function () {
  console.log('settimeout')
})
async1()
new Promise(function (resolve) {
  console.log('promise1')
  resolve()
}).then(function () {
  console.log('promise2')
})
console.log('script end')
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

输出结果:

script start
async1 start
async2
promise1
script end
async1 end
promise2
settimeout
1
2
3
4
5
6
7
8
  • 第 9 行,输出 'script start'
  • 第 10 行,向消息队列添加立即执行的 setTimeout 任务,该任务为宏任务,在 Promise 微任务执行完毕后再执行
  • 第 13 行,调用异步函数 async1,async1 开始执行,输出第 2 行的 'async1 start'
  • 第 3 行,await 后面为函数调用,为取得函数返回值,调用 async2
  • 第 7 行,async2 函数输出 'async2',并返回 undefined
  • 第 3 行,await 得到 undefined,为立即可用的值,在消息队列中将 undefined 值及恢复 async1 执行添加为任务,async1 暂停执行并退出
  • 第 14 行,新 Promise 开始执行,输出第 15 行的 'promise1'
  • 第 16 行结束,Promise 落定,在消息队列中添加 Promise 落定执行 then 的任务
  • 第 20 行,输出 'script end',至此,宏任务全部执行完毕
  • JS 从消息队列中取出 undefined 值和恢复 async1 执行的任务,恢复 async1 执行,程序回到第 3 行
  • 第 4 行,输出'async1 end',async1 返回
  • JS 从消息队列中取出 Promise 落定执行 then 的任务,程序回到第 17 行
  • 第 18 行,输出 'promise2',函数返回
  • 微任务执行完毕,JS 从消息队列中取出立即执行的 setTimeout 任务,程序回到第 11 行,并立即输出 'settimeout'

# 2. 解释以下异步代码的输出顺序

console.log(1);
new Promise((resolve, reject) => {
  console.log(2);
  resolve("a");
  console.log(3);
}).then(res => {
  console.log(4);
  setTimeout(() => {
    console.log(res);
    new Promise((resolve, reject) => {
      console.log(5);
      resolve("b");
      console.log(6);
    }).then(res => {
      console.log(res);
    });
    console.log(7);
  });
  console.log(8);
});
console.log(9);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

输出顺序:1 2 3 9 4 8 a 5 6 7 b

  • 程序运行,输出 1,进入 new Promise,输出 2,Promise 解决值为"a",输出 3
  • .then() 后面均为微任务,进入微任务队列,程序跳到最后一行,输出 9
  • 宏任务清空,执行微任务,输出 4
  • setTimeout 为宏任务,进入宏任务队列,程序跳到setTimeout 后一行,输出 8
  • 微任务清空,执行新的宏任务,输出 res 即 "a",并进入 new Promise,输出 5,Promise 解决值为 "b",输出 6
  • .then() 后面为微任务,进入微任务队列,程序跳到 setTimeout 最后一行,输出 7
  • 第二次宏任务清空,执行微任务,输出 "b"

# 3. 解释以下异步代码的输出顺序

const myPromise = Promise.resolve(Promise.resolve("Promise!"));

function funcOne() {
  myPromise.then(res => res).then(res => console.log(res));
  setTimeout(() => console.log("Timeout!"), 0);
  console.log("Last line!");
}

async function funcTwo() {
  const res = await myPromise;
  console.log(await res);
  setTimeout(() => console.log("Timeout!"), 0);
  console.log("Last line!");
}

funcOne();
funcTwo();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

输出顺序:Last line! Promise! Promise! Last line! Timeout! Timeout!

  • funcOne() 运行,将 myPromise 的解决值(即 "Promise!",原因见下)加入微任务队列
  • 将 setTimeout 回调加入新的宏任务队列,延时为立即执行
  • 输出 "Last line!",funcOne() 结束运行
  • 主线程宏任务没有结束,funcTwo() 运行
  • 遇到 await,将 myPromise 的解决值(即 "Promise!")加入微任务队列,函数退出运行
  • 此时主线程已经没有宏任务,开始执行微任务
  • 微任务 1:funcOne() 取得 myPromise 的解决值,即 "Promise!",形成一个解决值为 "Promise!" 的 Promise,继续调用 then,加入微任务队列
  • 微任务 2:funcTwo() 取得 myPromise 的解决值,即 "Promise!",函数恢复运行,赋值给 res;运行到 await res,将 res 的值加入微任务队列,函数退出运行
  • 微任务 3:funcOne() 取得新 Promise 的解决值,即 "Promise!",将其打印,输出 "Promise!"
  • 微任务 4:funcTwo() 取得 res 的值,即 "Promise!",函数恢复运行,将其打印,输出 "Promise!"
  • funcTwo() 继续运行,将 setTimeout 回调加入新的宏任务队列,延时为立即执行
  • 输出 "Last line!",funcTwo() 结束运行
  • 微任务队列已经清空,开始执行新的宏任务队列
  • 执行 funcOne() 中 setTimeout 的回调,输出 "Timeout!"
  • 执行 funcTwo() 中 setTimeout 的回调,输出 "Timeout!"

注意

当 Promise.resolve() 的参数仍是一个 Promise 时,等同于空包装,即 Promise.resolve(Promise.resolve("Promise!")) 全等于 Promise.resolve("Promise!"),并且新包装的 Promise 保持内层 Promise 的状态。

在 GitHub 中编辑此页 (opens new window)
上次更新于: 2022/11/3 23:25:22
this 指向问题
数学

← this 指向问题 数学→

Theme by Vdoing | Copyright © 2022-2022 Carlo | Powered by VuePress
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式