/** 事件循环(事件循环与异步有关)
- ~单线程模式
- js引擎是多线程,但引擎同时只执行一个任务,其它任务都必须再后面排队,即引擎只在一个线程上运行。这个线程称为主线程。
- ~事件循环机制
- js 本身并不慢,慢的是读写外部数据,比如ajax请求返回结果。如果等着ajax返回结果再出来,再往下执行,就会耗费很长的时间,
- 所以js设计出一种机制,CPU可以不管IO操作,而是挂起该任务,先执行后面的任务,等到IO操作返回了结果,再执行挂起任务。
- 同步任务执行完成后,引擎一遍又一遍检测那些挂起来的异步任务是否满足进入主线程的条件,这种循环检查的机制,叫做事件循环机制。
- ~任务队列
- js引擎运行时,除了一个正在运行的主线程,还提供一个或多个任务队列,里面是各种被挂起的异步任务。首先,主线程会去执行所有的同步任务,
- 等到同步任务开始全部执行完,就会去看任务队列里面的异步任务,如果满足条件,那么异步任务就会重新进入主线程开始执行,这时他就会变成同步
- 任务等到执行完,下一个异步任务再进入主线程,开始执行。一旦任务队列清空,程序就结束执行
宏任务队列:宏任务队列用于处理I/O和计时器等事件,每次执行一次
大概一些任务:setTimeout, setImmediate, setInterval, I/O, UI渲染
微任务队列(优先级高于宏任务): 微任务为async/await 和 Promise实现延迟执行,并且每个宏任务结束时执行。在每个事件循环前,微任务队列总是被清空
- 包含的api: process.nextTick, promise.then(), promise.catch(), promise.finally(), Object.observe, MutationObserver
注意:
1、每一个事件循环 event loop 都有一个微任务队列
2、每个 事件循环 event loop 会有一个或多个 宏任务队列
3、一个宏任务可以放入微任务队列中,也可以放入宏任务队列中
4、每一次事件循环, 首先会执行微任务队列,执行完成宏,会提取宏任务队列的一个任务加入微任务队列,接着继续执行微任务队列,然后这样依次执行下去,直到所有任务完成
运行程序 js异步执行机智:
1、js主线程拥有一个调用执行栈(同步任务)和一个任务队列,主线程会依次执行代码;
2、当遇到函数(同步)时,会先将改行数入栈,函数运行结束后再将该函数出栈;
3、当遇到 task任务(异步)时,task会返回一个值,让主线程不阻塞。真正task任务交给浏览器内核执行,执行后,会将该任务定义好的回调函数加入相应的任务队列中
4、js主线程执行完,并清空执行栈之后,会按照先进先出的方式读取任务队列中的回调函数,并将该函数入栈。继续运行执行栈,直到清空执行栈,再继续读取任务队列,循环结束
- 同步任务和异步任务
- 同步任务:没有被引擎挂起,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务。
- 异步任务:被引擎挂起,不进入主线程,而进入任务队列,只有引擎任务摸个异步任务可以执行了,该任务才会进入主程序执行。排在异步任务后面的代码,
- 不用等异步任务结束就会马上运行
作一点简单的总结:
- 在执行上下文栈的同步任务执行完后;
- 首先执行Microtask队列,按照队列
先进先出
的原则,一次执行完所有Microtask队列任务; - 然后执行Macrotask/Task队列,一次执行一个,一个执行完后,检测 Microtask是否为空;
- 为空则执行下一个Macrotask/Task;
- 不为空则执行Microtask
参考网站:https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/
实例代码
async function async1() {
console.log(‘async1 start’)
await async2()
console.log(‘async1 end’)
}
async function async2() {
console.log(‘async2’)
}
console.log(‘script start’)
setTimeout(function () {
console.log(‘settimeout’)
})
async1()
new Promise(function (resolve) {
console.log(‘promise1’)
resolve()
}).then(function () {
console.log(‘promise2’)
})
console.log(‘script end’)
打印答案
script start
async1 start
async2
promise1
script end
async1 end
promise2
settimeout