浏览器中的 envent loop 事件循环

191 阅读2分钟

1. js 单线程

js 单线程是因为是浏览器脚本语言,需要用户操作,同时只能有个操作DOM的行为,如果设计成多线程,一个线程添加DOM节点,另一个线程删除DOM节点,这个时候浏览器就为难了,哪个是准确的操作,为了避免这种复杂性,js设计成了单线程

2.任务队列

但是单线程同时带来了问题,所有执行任务需要排队,必须前一个任务执行完,才会执行后一个任务,如果前一个任务很耗时,后一个任务就不得不等着,所以在设计的时候挂起等待中的任务,先运行后面的任务,等有返回结果的时候回调函数放入任务队列中再由主线程执行

同步任务:主线程依次执行任务,执行完前一个再执行下一个

异步任务:不进入主线程执行栈,而是进入 任务队列,待有返回结果之后待主线程执行异步任务的回调函数

3. 事件循环

  1. 如图所示 js 执行栈当栈里有同步任务时候一直执行
  2. 当执行栈中没有同步任务,就会调用任务队列当中的异步任务
  3. 任务队列当中存在宏任务和微任务,如果微任务存在,就执行微任务直到微任务队列清空,再去执行宏任务
  4. 每次单个宏任务执行完毕后,检查微任务队列是否为空,如果不为空就会重复  第3步骤,然后再执行下一个宏任务,如此循环

举一个例子

console.log('start')
setTimeout(function() {
    console.log('setTimeout')
}, 0)
Promise.resolve()
.then(function() {
    console.log('promise1')
})
.then(function() {
    console.log('promise2')
})
console.log('end')

第一次循环, 执行同步代码,宏任务和微任务进入到各自队列中

MacroTasks: run script. setTimeout callback
MicroTasks: Promise.then()
JS stack: script
log: start, end

第二次循环, 微任务队列中先执行promise1,然后promise.then()加入微任务队列,继续执行为微任务队列输出  promise 2,然后微任务队列为空

MacroTasks:setTimeout callback
MicroTasks: Promise2.then()
JS stack: Promise2.then()
log: start, end, promise1,promise2

第三次循环,微任务

MacroTasks: setTimeout callback
MicroTasks: 
JS stack:
log: start, end, promise1, promise2, setTimeout

到此队列清空

可以参考这个帧动画