简谈javascript的Event Loop

267 阅读3分钟

对于非科班计算机毕业的童鞋来说。事件循环就像一个,怎么都不太能搞清的东西。今天我用自己理解的方式,带着问题聊聊。

js为什么设计为单线程?

  • js设计之初,希望能动态增删改查浏览器的dom节点。对元素做动态更改。
  • 如果js是多线程的,那么,任何时候,都可以共同操作同一个元素。
  • 对于浏览器来说,它并不清楚需要怎样操作,也不清楚需要先执行什么。而且,js没有事务,也不能加锁......
  • 为了避免这些繁琐的操作判断,将它设计为单线程。一步操作完成,才能进行下一步。

代码的执行顺序?

  • 在js的世界之初。众所周知,js执行代码的顺序,是从上置下,一行执行完成才能执行下一行代码。
  • 开心写代码的时候,发现逐行执行不能满足所有的情况。如:间隔固定时间让程序做什么事情;间隔固定时间,不发出停止指令的情况下,程序做某个事情;等待固定的信号后,程序做某些事情。。。等等等。
  • 在基础的从上置下不能满足的情况下,js就自己想了个办法,用了个小本本去记录,需要让它去做的事情。做完固定的事情之后,它会自动去看本子上还有没自己要做的事情,还有没有超时。
  • 这样形成一个js的事件循环。

如何更好的理解事件循环?

  • 打角色游戏的时候,总有一些任务需要去完成。玩家的基本操作,都是去完成主线,支线,副本的任务。
  • 那么从游戏的角度来看。从上置下执行的代码,更像是主线任务宏任务。其他也需要完成,但是优先级并没有那么高的,就像是支线任务微任务
  • 每次游戏,基本上都是先完成主线任务,在去做支线任务。像不像宏任务完成之后,去看微任务队列是否存在微任务。
  • 最终完整的事件流程,就变成了:
    • 执行script同步代码(script也属于宏任务)
    • 执行栈为空,查询是否有微任务需要执行
    • 执行所有的微任务
    • 必要的话渲染UI
    • 然后开始下一轮 Event loop,执行宏任务中的异步代码

事件循环的误区

  • setTimeout事件值设置为0,与 promise代码,谁先执行?
    setTimeout(
    ()=>{
        console.log('set');
    },0);
    new Promise(
    (r,i) => {
        console.log('p1');
        r('r1');
    }).then((r)=>{console.log(r)})
    
    • 结果是什么?
    • 代码从上往下执行,遇到 setTimeout,用小本本记到宏任务,遇到Promise,输出p1,其他的用小本本记到微任务里了。
    • 代码从上置下执行完成,去微任务队列查看是否有任务,输出r1
    • 继续执行宏任务异步代码setTimeout,输出set
    • 就是这么简单。

宏任务,微任务队员归类

  • 宏任务
    • script
    • setTimeout
    • setInterval
  • 微任务
    • process.nextTick
    • promise
    • Object.observe
    • MutationObserver