前言
最近一直在看基本知识点,发现自己的理解太过于偏颇、浅显加之记忆力不行,遂还是以文字的形式记录下来,这是缘由之一。当然,也希望通过这样的方式去整理逻辑,使之清晰。
为什么会有event loop?
和其他语言相比较,JavaScript语言特点就是单线程的。起初这门语言的设计主要用于与用户互动的,提高用户网页使用的体验度。然后渐渐发展成前端开发必不可少的语言。但是在实际的业务当中,开发人员发现单线程有太多的阻碍。这也是event loop产生的原因。
什么是event loop
主线程从“任务队列”中读取事件,这个过程是循环不断的,所以整个的这种运行机制成为even loop。
运行进制
首先需要了解JavaScript的运行机制
- 所有同步任务都在主线程上执行,形成一个执行栈。
- 主线程之外,还存在“任务队列”。只要异步任务有了运行结果,就在“任务队列”之中放置一个事件。
- 一旦“执行栈”中的所有同步任务执行完毕,系统就会读取“任务队列”,看看里面有哪些事件。那些对应的异步任务,于是接收等待状态,进入执行栈,开始执行。
- 主线任务不断重复上面的第三步。
再说even loop 运行进制。
主线任务会不断从任务队列中按顺序去取任务执行,每执行完一个任务都会检查microtask队列是否为空,如果不为空则会一次性执行完所有的microtask。然后再进入下一个循环去任务队列中去下一个任务执行。
具体如下:
- 当选择需要执行的宏任务队列,选择一个最先进入任务队列的宏任务,如果没有宏任务选择,则会跳转至microtask的执行步骤。
- 将事件循环的当前运行宏任务设置为已选择的宏任务。
- 运行宏任务。
- 将事件循环的当前运行任务设置为null。
- 将运行完的宏任务从宏任务队列中移除。
- microtask步骤:进入microtask检查点。
- 更新界面渲染。
- 返回第一步
执行进入microtask 检查的具体步骤:
- 设置进入microtask检查点的标志位true
- 当事件循环的微任务队列不为空时,选择一个最先进入microtask队列的microtask;设置事件循环的当前运行任务为null,将运行结束的microtask从microtask队列中移除。
- 对于相应事件循环的每个环境设置对象,通知他们哪些promise为rejected.
- 清理indexDB的事务。
- 设置进入microtask检查点的标志为false。
console.log('script start')
setTimeout(function(){
console.log('setTimeout --- 0')
},0)
setTimeout(function(){
console.log('setTimeout --- 200')
setTimeout(function(){
console.log('inner -setTimeout --- 0')
})
Promise.resolve().then(function(){
console.log('promise5')
})
},200)
Promise.resolve().then(function(){
console.log('promise1')
}).then(function(){
console.log('promise2')
})
Promise.resolve().then(function(){
console.log('promise3')
})
console.log('script end')
运行结果:
script start
script end
promise1
promise3
promise2
setTimeout --- 0
setTimeout --- 200
promise5
inner -setTimeout --- 0
分析:
- 首先顺序执行完主进程上的同步任务,第一句和最后一句的console.log
- 接着遇到setTimeout 0,它的作用是在 0ms 后将回调函数放到宏任务队列中(这个任务在下一次的事件循环中执行)。
- 接着遇到setTimeout 200,它的作用是在 200ms 后将回调函数放到宏任务队列中(这个任务在再下一次的事件循环中执行)。
- 同步任务执行完之后,首先检查微任务队列, 即 microtask队列,发现此队列不为空,执行第一个promise的then回调,输出 'promise1',然后执行第二个promise的then回调,输出'promise3',由于第一个promise的.then()的返回依然是promise,所以第二个.then()会放到microtask队列继续执行,输出 'promise2';
5.此时microtask队列为空,进入下一个事件循环, 检查宏任务队列,发现有 setTimeout的回调函数,立即执行回调函数输出 'setTimeout---0',检查microtask 队列,队列为空,进入下一次事件循环. - 检查宏任务队列,发现有 setTimeout的回调函数, 立即执行回调函数输出'setTimeout---200'
- 接着遇到setTimeout 0,它的作用是在 0ms 后将回调函数放到宏任务队列中,检查微任务队列,即 microtask 队列,发现此队列不为空,执行promise的then回调,输出'promise5'。
- 此时microtask队列为空,进入下一个事件循环,检查宏任务队列,发现有 setTimeout 的回调函数,立即执行回调函数输出,输出'inner-setTimeout---0'。代码执行结束。
网友评论