js之EventLoop

48 阅读2分钟

众所周知js是一门单线程的非阻塞脚本语言,这表明在同一时间最多只有一个代码块在执行。 那么js为什么是单线程的呢?因为我们经常使用js来操作dom,如果同时允许执行多段代码,都对同一个dom进行不同的操作,计算机就不知道怎么操作了。所以js被设计成是单线程,那非阻塞是怎么体现呢?如果js执行是阻塞的,那么当js发起一个异步请求时,在请求未返回前我们就会一直等待请求结果,后面的代码就无法执行,浏览器也就跟着没反应,所以js是非阻塞的,那么它是如何实现异步控制的呢,靠的就是事件循环

image.png 如图,浏览器内核也称渲染进程,浏览器主要包括(主进程,GPU渲染进程,插件进程,渲染进程),渲染进程主要包括图示的5个现场

时间循环过程

  1. 一开始整个脚本作为一个宏任务执行
  2. 脚本中的同步代码直接执行,js引擎执行代码时可能会触发http请求线程,定时器线程(setTimeout,setInterval等),事件触发线程(click,ajax等)。宏任务会依次进入宏任务队列,微任务依次进入微任务队列
  3. js会一直监听任务队列,每执行完一个宏任务,就会执行这个宏任务下所有的微任务队列,执行完后出触发gui渲染线程,至此为一帧
  4. 接着执行宏任务队列的下一个任务,当前宏任务执行完后执行此宏任务下所有的微任务队列,执行完触发渲染线程进行渲染
  5. 直到所有的宏微任务全部执行完毕

常常接触到的宏任务一般有

  • 整体代码
  • setTimeout/setInterval
  • I/O

微任务一般是promsie,nodejs环境下的nextTick

看一道简单的题目

console.log('script start')

setTimeout(function () {
  console.log('setTimeout')
}, 0)

Promise.resolve()
  .then(function () {
    console.log('promise1')
  })
  .then(function () {
    console.log('promise2')
  })
console.log('script end')

1.整体代码作为第一个宏任务进入主线程,同步代码执行输出script start 2.执行setTimeout,进入宏任务队列 3.Promise.resolve().then().then()统统进入微任务队列 4.执行同步代码输出script end 5.这一次的宏任务(整个代码)执行完,接下来执行这一个宏任务下所有的微任务队列即promise.then输出promise1,再输出promise2 6.接着执行宏任务队列中的下一个宏任务setTimeout输出setTimeout 7.执行完毕