我们说到JS的事件循环机制同时不得不提到浏览器的事件循环机制
1. 浏览器的事件循环机制
1.1 事件执行的顺序
(1),当事件开始时,首先会进入JS主线程机制,由于JS属于单线程机制,因此存在多个任务的时候会存在等待的情况,先等待最先进入线程的事件处理完毕
(2),这样就会出现等待的情况,如果之前的事件没有执行完成,后面的事件就会一直等待
(3),但是类似于AJAX和setTimeout , setInterval 等待的事件,就出现了异步处理
(4),通过将异步的事件交给异步模块处理,主线程就会去并行的处理后面的事件
(5),当主线程空闲的时候,异步处理完成,主线程就会读取异步处理返回的callback执行异步事件后续的操作
同步执行就是主线程按照事件的顺序,依次执行事件 异步执行就是主线程先跳过等待,执行后面的事件,异步事件交给异步模块处理
如图所示:讲解同步执行和异步执行的任务执行流程。 1,事件开始,进入主线程 2,主线程如果发现异步事件,将异步事件移交给异步模块,主线程继续执行后面的同步事件 3,当异步进程执行完毕之后,再回到事件队列中 4,主线程执行完毕,就会查询事件队列,如果事件队列中存在事件,事件队列就将事件推到主线程 5,主线程执行事件队列推过来的事件,执行完毕后再去事件队列中查询… 这个循环的过程就是事件循环
1.2 异步任务又分为宏任务(macrotask)和微任务(microtask)
常见的宏任务:setTimeout setInterval I/O script
常见的微任务:promise
同一事件循环中,微任务永远在宏任务之前 同一事件循环中,微任务永远在宏任务之前 同一事件循环中,微任务永远在宏任务之前
(**重要的事情说三遍!)
案列:1
console.log(1)
setTimeout(() => {
console.log(2)
}, 0)
Promise.resolve().then(()=> {
console.log(3)
})
console.log(4)
1,任务开始执行,进入执行栈。遇到console.log(1),是同步任务,输出1; 2,执行栈继续执行,遇到定时器setTimeout,是异步宏任务,进入异步进程的宏任务队列; 3,执行栈继续执行,遇到Promise,是异步微任务,进入异步进程的微任务队列; 4,执行栈继续执行,遇到console.log(4),是同步任务,输出4; 5,同步任务执行完成,开始查询任务队列,微任务队列在宏任务队列之前,先执行微任务队列输出3; 4,微任务队列执行完毕,再查询宏任务队列,输出2,至此整个任务队列完成,最后输出1 4 3 2
案列:2
setTimeout(function(){
console.log('定时器开始啦')
});
new Promise(function(resolve){
console.log('马上执行for循环啦');
for(var i = 0; i < 10000; i++){
i == 99 && resolve();
}
}).then(function(){
console.log('执行then函数啦')
});
console.log('代码执行结束');
结果:马上执行for循环啦 --- 代码执行结束 --- 执行then函数啦 --- 定时器开始啦
1,首先执行script下的宏任务,遇到setTimeout,将其放到宏任务的【队列】里 2,遇到 new Promise直接执行,打印"马上执行for循环啦" 3,遇到then方法,是微任务,将其放到微任务的【队列里】 4,打印 “代码执行结束” 5,本轮宏任务执行完毕,查看本轮的微任务,发现有一个then方法里的函数, 打印"执行then函数啦" 到此,本轮的event loop 全部完成。 6,下一轮的循环里,先执行一个宏任务,发现宏任务的【队列】里有一个 setTimeout里的函数,执行打印"定时器开始啦"
案列:3
console.log(1)
setTimeout(() => {
console.log(2)
}, 0)
new Promise((resolve) => {
console.log(3)
resolve(4)
}).then((res)=> {
console.log(res)
})
console.log(5)
案列:4
setTimeout(() => console.log('setTimeout1'), 0)
setTimeout(() => {
console.log('setTimeout2')
Promise.resolve().then(() => {
console.log('promise3')
Promise.resolve().then(() => {
console.log('promise4')
})
console.log(5)
})
setTimeout(() => console.log('setTimeout4'), 0)
}, 0)
setTimeout(() => console.log('setTimeout3'), 0)
Promise.resolve().then(() => {
console.log('promise1')
})
1.3 遇到async和await的情况
一旦遇到await 就立刻让出线程,阻塞后面的代码,先执行async外面的同步代码 等候之后,对于await来说分两种情况:不是promise 对象;是promise对象
1,如果不是promise,await会阻塞后面的代码,先执行async外面的同步代码,同步代码执行完毕后,在回到async内部,把promise的东西,作为await表达式的结果 2,如果它等到的是一个 promise 对象,await 也会暂停async后面的代码,先执行async外面的同步代码,等着 Promise 对象 fulfilled,然后把 resolve 的参数作为 await 表达式的运算结果。 3,如果一个 Promise 被传递给一个 await 操作符,await 将等待 Promise 正常处理完成并返回其处理结果。
下面是实际遇到的案列仅供参考:
案列:1 (没有 async await)代码依次执行
function fn1() {
return 1
}
function fn2 () {
console.log(2)
console.log(fn1())
console.log(3)
}
fn2()
console.log(4)
案列:2(没有promise)如果不是promise,await会阻塞后面的代码,先执行async外面的同步代码,同步代码执行完毕后,在回到async内部,把promise的东西,作为await表达式的结果
async function fn1() {
return 1
}
async function fn2 () {
console.log(2)
console.log(await fn1())
console.log(3)
}
fn2()
console.log(4)
案列:3(存在promise) 如果它等到的是一个 promise 对象,await 也会暂停async后面的代码,先执行async外面的同步代码,等着 Promise 对象 fulfilled,然后把 resolve 的参数作为 await 表达式的运算结果。
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)
案列: 4
async function async1() {
console.log( 'async1 start' )
await async2()
console.log( 'async1 end' )
}
async function async2() {
console.log( 'async2' )
}
async1()
console.log( 'script start' )
async1 start
async2
script start
async1 end
一旦遇到await 就立刻让出线程,阻塞后面的代码 等候之后,对于await来说分两种情况 不是promise 对象 是promise对象 如果不是promise,await会阻塞后面的代码,先执行async外面的同步代码,同步代码执行完毕后,在回到async内部,把promise的东西,作为await表达式的结果 如果它等到的是一个 promise 对象,await 也会暂停async后面的代码,先执行async外面的同步代码,等着 Promise 对象 fulfilled,然后把 resolve 的参数作为 await 表达式的运算结果。 如果一个 Promise 被传递给一个 await 操作符,await 将等待 Promise 正常处理完成并返回其处理结果。 1.4 综合上面的案列我们大概知道了异步任务执行顺序了。 操练试一试:
async function t1 () {
console.log(1)
console.log(2)
await new Promise(resolve => {
setTimeout(() => {
console.log('t1p')
resolve()
}, 1000)
})
await console.log(3)
console.log(4)
}
async function t2() {
console.log(5)
console.log(6)
await Promise.resolve().then(() => console.log('t2p'))
console.log(7)
console.log(8)
}
t1()
t2()
console.log('end')
|