1.1 事件执行的顺序
(1)当事件开始时,首先会进入JS主线程机制,由于JS属于单线程机制,因此存在多个任务的时候会存在等待的情况,先等待最先进入线程的事件处理完毕
(2)这样就会出现等待的情况,如果之前的事件没有执行完成,后面的事件就会一直等待
(3)但是类似于AJAX和setTimeout , setInterval 等待的事件,就出现了异步处理
(4)通过将异步的事件交给异步模块处理,主线程就会去并行的处理后面的事件
(5)当主线程空闲的时候,异步处理完成,主线程就会读取异步处理返回的callback执行异步事件后续的操作
同步执行就是主线程按照事件的顺序,依次执行事件
异步执行就是主线程先跳过等待,执行后面的事件,异步事件交给异步模块处理
图解如下:
通俗来说就是相当于在车站买票排队,一个售票员同一时间只可以办理一个人的业务(这就是主线程),这时候就需要客户进行等待排队(排队就是任务队列/事件队列),这就类比于js的单线程机制 。
1.2 异步任务又分为宏任务(macrotask)和微任务(microtask)
常见的宏任务:setTimeout setInterval I/O script
常见的微任务:promise
同一事件循环中,微任务永远在宏任务之前
console.log('我是同步任务1')
setTimeout(()=>{
console.log("我是宏任务1")
},0)
Promise.resolve().then(()=>{
console.log('我是微任务1')
})
console.log('我是同步任务2')
打印出的结果如下:
可以看出同步任务排在异步任务之前处理,异步任务是微任务优先与宏任务执行。
console.log('我是同步任务1')
setTimeout(()=>{
console.log("我是宏任务1")
},0)
new Promise((resolve)=>{
console.log('我是同步任务2')
resolve(4)
}).then((res)=>{
console.log("我是微任务1")
})
console.log('我是同步任务3')
打印出的结果如下:
注意:对于promise它的异步是通过单线程非阻塞的方式实现的,then之后才是异步操作,前面的还是同步操作。关于单线程非阻塞的理解可以参考链接 js异步的理解 。
1.3 遇到async
和await
的情况
一旦遇到await 就立刻让出线程,阻塞后面的代码,先执行async外面的同步代码
等候之后,对于await来说分两种情况:不是promise 对象;是promise对象
(1)没有 async
await的情况
function fn1() {
return 1
}
function fn2() {
console.log(2)
console.log(fn1())
console.log(3)
}
fn2()
console.log(4)
// 结果 2 1 3 4
可以看出此时代码是依次执行的。
(2)await后面跟的不是promise对象的情况
举例代码如下:
async function fn1(){
return 1
}
async function fn2(){
console.log(2)
console.log(await fn1())
console.log(3)
}
fn2()
console.log(4)
输出的结果如下:
结论:当await后面跟的不是promise对象时,此时遇到await就会阻塞后面的代码,优先执行async外面的同步代码,当外面的同步代码都执行完毕后才开始执行await代码以及它后面的代码。
(3)await后面跟的是promise对象的情况
举例代码如下:
function fn1() {
return new Promise((reslove) => {
reslove(1)
})
}
async function fn2() {
console.log(2)
console.log(await fn1())
console.log(3)
}
fn2()
console.log(4)
结果如下
结论:从结果可以看出当await后面跟的是promise对象时,遇到await也会阻塞它以及它后面的代码,会先执行asnyc外面的同步代码,当同步代码全部执行完且等promise对象 fulfilled后,然后把 resolve
的参数作为 await
表达式的运算结果。