概念
js是单线程的,也就代表js只能一件事情一件事情执行,那如果一件事情执行时间太久,后面要执行的就需要等待,需要等前面的事情执行完成,后面的才会执行。
所以为了解决这个问题,js委托宿主环境(浏览器)帮忙执行耗时的任务,执行完成后,在通知js去执行回调函数,而宿主环境帮我们执行的这些耗时任务也就是异步任务
js本身是无法发起异步的,但是es5之后提出了Promise可以进行异步操作
执行流程
- 主线程先判断任务类型
- 如果是同步任务,主线程自己执行
- 如果是异步任务,交给宿主环境(浏览器)执行
- 浏览器进行异步任务的执行,每个异步执行完后,会将回调放进任务队列,先执行完成的先放进任务队列,依次放入
- 等主线程任务全部执行完后,发现主线程没有任务可执行了,会取任务队列中的任务,由于任务队列里是依次放入进来的,所以取得时候也会先取先进来的,也就是先进先出原则
- 在任务队列中取出来的任务执行完后,在取下一个,依次重复,这个过程也称为eventLoop 事件轮训
宏任务
由宿主环境发起的异步:宏任务
setTimeOut、setInterval、特殊的(代码块、script)
setTimeOut
setInterval
setImmediate
微任务
由javascript自身发起的异步:微任务
执行顺序
- 先执行宏任务
- 宏任务执行完后看微任务队列是否有微任务
- 没有微任务执行下一个宏任务
- 有微任务将所有微任务执行
- 执行完微任务,执行下一个宏任务
练习案例
案例1:
console.log(1)
setTimeout(function(){
console.log(2)
}, 0)
new Promise(function(resolve){
console.log(3)
resolve()
}).then(function(){
console.log(4)
})
console.log(5)
答案:1、3、5、4、2
解析:
-
主线程判断是同步代码还是异步代码
console.log(1) // 同步任务 setTimeout(function(){ console.log(2) // 异步任务:宏任务 }, 0) new Promise(function(resolve){ console.log(3) // 同步任务 resolve() }).then(function(){ console.log(4) // 异步任务:微任务 }) console.log(5) // 同步任务
-
执行同步任务
console.log(1) // 同步任务 console.log(3) // 同步任务 console.log(5) // 同步任务
-
执行异步任务:微任务
console.log(4) // 异步任务:微任务
-
执行异步任务:宏任务
console.log(2) // 异步任务:宏任务
案例2:
注意点:await的执行顺序为从右到左,会阻塞后面的代码执行,但并不是直接阻塞await的表达式
await下面(下面不是右面)的代码可以理解为promise.then(function(){ 回调执行的 })
async function async1() {
console.log('async1 start')
await async2()
// await后面的代码可以理解为promise.then(function(){ 回调执行的 })
console.log('async1 end')
}
async function async2() {
console.log('async2')
}
console.log('script start')
setTimeout(function() {
console.log('setTimeout')
}, 0)
async1()
console.log('script end')
答案:script start、async1 start、async2、script end、async1 end、setTimeout
解析:
-
主线程判断同步异步
async function async1() { console.log('async1 start') await async2() // 同步任务 // await后面的代码可以理解为promise.then(function(){ 回调执行的 }) console.log('async1 end') // 异步任务:微任务 } async function async2() { console.log('async2') } console.log('script start') // 同步任务 setTimeout(function() { console.log('setTimeout') // 异步任务:宏任务 }, 0) async1() // 同步任务 console.log('script end') // 同步任务
-
执行同步任务
console.log('script start') console.log('async1 start') console.log('async2') console.log('script end')
-
执行异步任务:微任务
console.log('async1 end') console.log('setTimeout')
案例3:
console.log(1)
setTimeout(function(){
console.log(2)
}, 2000)
new Promise(function(resolve){
console.log(3)
resolve()
}).then(function(){
console.log(4)
})
setTimeout(function(){
console.log(5)
new Promise(function(resolve){
console.log(6)
resolve()
}).then(function(){
console.log(7)
})
}, 3000)
setTimeout(function(){
console.log(8)
new Promise(function(resolve){
console.log(9)
resolve()
}).then(function(){
console.log(10)
})
}, 1000)
答案:1、3、4、8、9、10、2、5、6、7
解析:
-
区分同步任务和异步任务
console.log(1) // 同步任务 setTimeout(function(){ console.log(2) // 异步任务 }, 2000) new Promise(function(resolve){ console.log(3) // 同步任务 resolve() }).then(function(){ console.log(4) // 异步任务 }) setTimeout(function(){ console.log(5) // 异步任务 new Promise(function(resolve){ console.log(6) resolve() }).then(function(){ console.log(7) }) }, 3000) setTimeout(function(){ console.log(8) // 异步任务 new Promise(function(resolve){ console.log(9) resolve() }).then(function(){ console.log(10) }) }, 1000)
-
执行同步任务
console.log(1) console.log(3)
-
异步任务添加到不同任务队列中
微任务添加到微任务队列[ console.log(4) ]
new Promise(function(resolve){ console.log(3) resolve() }).then(function(){ console.log(4) // 微任务 })
宏任务
由宿主发起异步,异步完成将回调放入宏任务队列
setTimeout(function(){ console.log(2) // 异步任务 }, 2000) setTimeout(function(){ console.log(5) // 异步任务 new Promise(function(resolve){ console.log(6) resolve() }).then(function(){ console.log(7) }) }, 3000) setTimeout(function(){ console.log(8) // 异步任务 new Promise(function(resolve){ console.log(9) resolve() }).then(function(){ console.log(10) }) }, 1000)
进入宏任务队列
// 宏任务1: function(){ console.log(8) // 异步任务 new Promise(function(resolve){ console.log(9) resolve() }).then(function(){ console.log(10) }) } // 宏任务2: function(){ console.log(2) // 异步任务 } // 宏任务3: function(){ console.log(5) // 异步任务 new Promise(function(resolve){ console.log(6) resolve() }).then(function(){ console.log(7) }) }
-
执行微任务[]
console.log(4)
-
微任务已全部执行完成,接下来执行下一个宏任务
// 宏任务1: function(){ console.log(8) new Promise(function(resolve){ console.log(9) resolve() }).then(function(){ console.log(10) // 进入微任务 }) }
console.log(8) console.log(9)
-
console.log(10)进入微任务:[console.log(10)]
console.log(10) // 微任务[]
-
微任务空,执行下一个宏任务
// 宏任务2: function(){ console.log(2) // 异步任务 }
console.log(2)
-
微任务中还是空,继续执行下一个宏任务
function(){ console.log(5) // 异步任务 new Promise(function(resolve){ console.log(6) resolve() }).then(function(){ console.log(7) // 进入微任务 }) }
console.log(5) console.log(6)
-
微任务中有[console.log(7)]
console.log(7)
-
最后微任务清空,宏任务也清空