事件循环Event Loop

时间:2021-1-8 作者:admin

一句话解释

js的主线程是单线程运行的,主线程先执行同步代码,遇到异步操作如ajax请求、定时器等需要在一段时间后再执行的事件,便交给其他线程如请求线程、定时触发线程,满足条件后就放到任务队列中,主线程执行完任务后,就去任务队列里拉取任务,按照先进先出的原则依次执行,如此循环;
其中任务队列分为宏任务队列与微任务队列,执行完一个宏任务都会去检查是否有微任务,执行完所有微任务后,页面更新渲染,再执行下一个宏任务;

问答

1.为什么JS是单线程的?

  • 防止同时操作同一DOM出现混乱。
  • 所谓的”JS是单线程的”只是指JS的主运行线程只有一个,而不是整个运行环境都是单线程。

2.JS单线程如何实现异步?

  • JS异步的实现靠的就是浏览器的多线程,当遇到异步API时,就将这个任务交给对应的线程,当这个异步API满足回调条件时,对应的线程又通过事件触发线程将这个事件放入任务队列,然后主线程从任务队列取出事件继续执行。
  • JS引擎线程跟GUI线程是互斥的,一个执行就另一个不执行,如果JS长时间运行,GUI线程就不能执行,整个页面就感觉卡死了。

3.为什么要有微任务?

  • 微任务是为了解决效率和实时性问题,处理高优先级的任务。

4.常见的宏任务与微任务有哪些?

  • 宏任务macrotask:script(整体代码)、setTimeout、setInterval、setImmediate、I/O
  • 微任务microtask:Promise、MutaionObserver、process.nextTick(Node)

5.浏览器什么时候渲染?

  • 执行完一个宏任务以及当前所有微任务后渲染一次;
  • 但并不是每轮event loop都会更新渲染,这取决于是否修改了dom和浏览器觉得是否有必要在此时立即将新状态呈现给用户。如果在一帧的时间内(时间并不确定,因为浏览器每秒的帧数总在波动,16.7ms只是估算并不准确)修改了多处dom,浏览器可能将变动积攒起来,只进行一次绘制。

6.与Node环境的事件循环有什么区别?

浏览器和Node 环境下,microtask 任务队列的执行时机不同:

  • 浏览器端,microtask 在事件循环的 macrotask 执行完之后执行;
  • Node版本>=11,和浏览器表现一致;
  • Node版本<11,microtask 在事件循环的各个阶段之间执行,也就是一个阶段执行完毕,就会去执行microtask队列的任务;

由于定时器插入时机不同,同一代码执行结果也会不同,如下:

setTimeout(()=>{
    console.log('timer1')
    Promise.resolve().then(function() {
        console.log('promise1')
    })
}, 0)
setTimeout(()=>{
    console.log('timer2')
    Promise.resolve().then(function() {
        console.log('promise2')
    })
}, 0)

Node端分为6个阶段,

  • timers 阶段:这个阶段执行timer(setTimeout、setInterval)的回调
  • I/O callbacks 阶段:处理一些上一轮循环中的少数未执行的 I/O 回调
  • idle, prepare 阶段:仅node内部使用
  • poll 阶段:获取新的I/O事件, 适当的条件下node将阻塞在这里
  • check 阶段:执行 setImmediate() 的回调
  • close callbacks 阶段:执行 socket 的 close 事件回调

外部输入数据–>轮询阶段(poll)–>检查阶段(check)–>关闭事件回调阶段(close callback)–>定时器检测阶段(timer)–>I/O事件回调阶段(I/O callbacks)–>闲置阶段(idle, prepare)–>轮询阶段(按照该顺序反复运行)

参考

声明:本文内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎进行举报,并提供相关证据,工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。