事件循环、Promise、async / await 怎么执行

先理解原理和示例,再练习完整口述与追问。

浏览器中先执行当前同步任务,在微任务检查点处理微任务;渲染还取决于浏览器的渲染机会,不是每个任务结束都重绘。await 之后的继续执行具有异步调度行为,Promise 构造器执行器本身同步执行。

不要把浏览器和 Node 的调度规则混用;微任务不断追加也可能阻塞渲染。MDN:JavaScript 执行模型

先区分执行回调与登记回调:Promise 构造器中的 executor 立即同步执行,then 登记的反应稍后进入微任务;async 函数先同步执行到第一个 await,await 后续部分通过 Promise 调度恢复。浏览器完成一个任务后会检查微任务,清空后才有机会进行渲染和后续任务。定时器满足延迟条件只意味着可以被调度,不保证此刻立即执行。

js
console.log('A')
setTimeout(() => console.log('B'), 0)
Promise.resolve().then(() => console.log('C'))
async function run() {
  console.log('D')
  await 0
  console.log('E')
}
run()
console.log('F')
// 常见浏览器中:A D F C E B

同步阶段输出 A、D、F;先登记 C,再登记 await 的继续执行 E,最后轮到定时器 B。

不要把所有异步都叫宏任务,也不要认为 await 会阻塞整个线程。递归排入微任务可能持续占用主线程;Node 还存在 nextTick 和不同事件循环阶段,不能直接套用浏览器输出。

浏览器先执行当前任务的同步调用栈,栈清空并到达微任务检查点后处理微任务,执行期间新加入的微任务也要继续处理。Promise 构造器的执行器同步执行,then 回调异步执行;await 会暂停当前 async 函数,后续代码经异步调度继续。定时器到时只是具备排队条件,不保证精确时间。渲染由浏览器决定合适机会,不是每个任务结束都必然绘制;无限追加微任务也会让界面卡住。