解析事件循环机制(node、浏览器)

348 阅读3分钟

一、为什么会有event loop?

js的任务分为同步和异步两种:
1、同步任务是直接放在主线程上排队依次执行;
2、异步任务会放在任务队列中,若有多个异步任务则需要在任务队列中排队等待,
   任务队列类似于缓冲区,任务下一步会被移到调用栈然后主线程执行调用栈的任务。
   
调用栈:是一个栈结构,函数调用会形成一个栈帧,帧中包含了当前执行函数的参数和局部变量等上下文信息,
函数执行完成后,它的执行上下文会从栈中弹出。

js是单线程的,单线程是指js引擎中解析和执行js代码的线程只有一个,每次只做一件事,
而ajax请求中,主线程在等待响应的过程中回去做其他的事情,浏览器先在事件表注册ajax的回调函数,
响应回来后回调函数被添加到任务队列中等待执行,不会造成线程阻塞,这种方式是异步的。

所以,检查调用栈是否为空以及将某个任务添加到调用栈中的过程就是event loop,这就是js实现异步的核心。

二、浏览器中的Event Loop

1、micro-task和macro-task

浏览器端事件循环中的异步队列有两种:micro(微任务队列)、macro(宏任务队列);

常见的宏任务:setTimeout、setInterval、script(整体代码)、I/O操作、UI渲染等;

常见的微任务:new Promise().then()、MutationObserve、process.nextTick()等;

2、事件循环的过程

  检查宏任务队列是否为空,非空则执行宏任务队列中的一个任务,
  为空则继续检查微任务队列是否为空,
  非空则取出微任务队列中的任务执行,执行完成继续检查微任务对列,微任务队列为空则执行视图更新。
  

三、node中的event loop

1、node中的事件循环和浏览器中的是完全不相同的东西。nodejs采用v8作为js的解析引擎, I/O处理方面使用了自己设计的libuv,libuv是一个基于事件驱动的跨平台抽象层,封装了不同操作系统的底层特性,对外提供统一的api,事件循环机制也是它里面实现的。

2、node的运行机制如下:

1、v8引擎解析js脚本;
2、解析后的代码调用node api;
3、libuv库负责node API的执行。它将不同的任务分给不同的线程,形成一个事件循环,
以异步的方式将任务的执行结果返回給v8引擎;
4、v8引擎再将结果返回給用户。

3、六大阶段

libuv引擎中的事件循环分为6个阶段,它们会按照顺序反复运行。

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