前置知识
js是一门单线程语言, 单线程的意思是 js执行环境中负责执行代码的线程只有一个
js最早是一门运行在浏览器端的脚本语言, 目的是为了实现页面上的动态交互, 核心就是 DOM 操作, 这就决定了他必须使用单线程模式工作 (多线程的情况下, 我在一个线程修改dom, 另一个线程删除dom就会出现线程同步问题)
一个线程一次就只能执行一个问题, 多个问题需要处理的情况就需要排队, 安全简单, 但容易阻塞
js的异步模式:
- 不会等待这个任务的结束才开始下一个任务
- 对于耗时操作开启过后就立即往后执行下一个任务
- 耗时任务的后续逻辑一般通过回调函数的方式定义, 耗时任务完成过后就会自动执行传入的回调
console.log('global begin')
setTimeout(function(timer1(){
console.log('timer1 invoke')
},1800)
setTimeout(function timer2(){
console.log('timer2 invoke')
setTimeout(function inner(){
console.log('inner invoke')
},1000)
},1000)
console.log('global end')
setTimeout 是异步调用, 内部的API环境就是为回调函数开启了一个倒计时器,放到一边,这里的倒计时器是单独工作的, 并不会受js线程影响, 在web中对应线程开启, 计时结束后就将回调函数推入事件队列, 等待主执行栈空闲的时候读取调用, ajax, dom操作类似
宏任务 / 微任务
宏任务: script整体代码, setTimeout, setInterval…
微任务: promise & process.nextTick() & mutationobserver…
微任务就像是你在移动营业厅办理业务, 完事之后你突然想起来没得花费了,要冲一下, 这时候业务员也会马上给你办理, 不需要你重新去排队, 所以微任务会在当前主代码执行完毕之后立即执行
而宏任务会在当前主代码及与之相关的微任务执行完之后, 去读取任务队列执行, 在执行宏任务代码中产生的微任务又会在任务执行完后立即执行, 如此循环
Promise即便没有任何操作, 他的回调也是异步的
废话不说先上题吧
async function async1() {
console.log('async1 start')
await async2()
console.log('async1 end')
}
async function async2() {
console.log('async2 start')
await async3()
console.log('async2 end')
}
async function async3() {
console.log('async3 start')
}
setTimeout(() => {
console.log('setTimeout')
}, 0)
console.log('script start')
async1()
new Promise((resolve) => {
console.log('promise1')
setTimeout(resolve, 0)
}).then(() => {
console.log('promise1 then')
})
new Promise((resolve) => {
console.log('promise2')
resolve()
}).then(() => {
console.log('promise2 then')
})
console.log('script end')
- 代码从上往下执行, 遇到setTimeout放入宏任务队列
- 输出
script start
- 调用函数
async1
, 输出async1 start
, 往下执行, - 进入
async2
, 输出async2 start
- 进入
async3
, 输出async3 start
- async3 调用结束, 后续代码进入微任务队列
- 代码继续往下执行, 遇到 new Promise, 输出
promise1
- 遇到
setTimeout(resolve,0)
, 这行代码是将 promise 的完成放在了一个0秒的计时器中, 该代码被放入宏任务队列排队 - 继续往下执行, 输出
promise2
, resolve() 改变了promise的状态, then方法回调推入微任务对列 - 继续往下执行, 输出
script end
- 主线程执行完毕, 读取微任务继续执行
- 首先输出
async2 end
, 此时 async1中 async2的调用结束, 后续代码推入微任务队列, 继续执行当前微任务队列的下一个微任务 - 输出
promise2 then
, 继续微任务队列 - 输出
async1 end
, 当前微任务队列执行完毕为空, 执行下一个宏任务 - 执行第一个setTimeout, 输出
setTimeout
- 当前微任务队列为空, 执行下一个宏任务
- 执行
setTimeout(resolve, 0)
, 结束当前promise, 后续回调进入微任务队列 - 读取微任务队列任务, 输出
promise1 then