Event loop的化繁为简(一)

713 阅读3分钟

最近在系统的学习Node.js,在学习过程中遇到了Event-loop这个东东,下面把我自己的一些心得进行总结,分享给大家。

Javascript真的是单线程运行吗?

了解Javascript的同学应该知道Javascript是单线程运行的,比如在代码里你打个alert,系统给你弹个框,后面的代码都给阻塞了,事实上也确实如此。因为如果系统给你new Thread的功能,二条线程同时更新一个dom,那不打架了么~~~。

可是,单线程的运行模式是很不友好的,在很多场景下,都需要异步来提高用户体验,于是老美那帮哥们就发明了Promise、Callback,setTimeoutt,setInterval,还有我们最常用的Ajax(XMLHttpRequest类) 等异步编程接口,让我们能开发出,更好用户体验的产品。

那么问题又来了,如果在一个项目里实用了这些异步编程接口,他们是如何工作的,而且如何有效的控制他们的运行先后顺序,这个我们必须要搞清楚,下面就来简单分享一下。

在开始之前先谈谈【进程与线程】

为什么要说进程与线程,我用最简单通俗的话来讲,大家开发用的浏览器Chrome,是一个多进程的浏览器;大家在开发时写的setTimeout,setInterval, XMLHttpRequest类,是在一个进程里开辟的新的线程。

好混乱。别急。画图。

chrome的一个切页,对应一个进程

从上图我们可以看到,一个Chrome切页,就是一个网页,也就是一个进程。同时在一个网页里,我们会写很多代码,里面有很多API调用,如Promise、Callback,setTimeoutt,setInterval,Ajax(XMLHttpRequest类),它们会开启自己的一个线程去运行,网页(进程)里包含多个线程

  • 浏览器事件触发线程(用来控制事件循环,存放setTimeout、浏览器事件、ajax的回调函数)
  • 定时触发器线程(setTimeout定时器所在线程)
  • 异步HTTP请求线程(ajax请求线程)

浏览器的事件循环(Event-loop)

浏览器的事件循环
借用网上的图片。工作原理总结如下:

  1. 所有同步任务都在栈内存图中的(Stack框)的主线程上执行,形成一个执行栈,执行栈也表示作用域栈。
  2. 主线程之外,还存在一个任务队列(callback quere框)。只要异步任务(一个个线程)有了运行结果,就在任务队列之中放置一个事件(回调函数)。
  3. 一旦执行栈中的所有同步任务执行完毕,系统就会读取任务队列,将队列中的事件(回调函数)放到执行栈中依次执行
  4. 主线程从任务队列中读取事件,这个过程是循环不断的。

浏览器的任务队列运行方式

Node.js的事件循环(Event-loop)

Node.js的事件循环
借用网上的图片。工作原理总结如下:

  1. 我们写的node.js代码会交给v8引擎进行处理。
  2. 代码中可能会调用node.js Api(如:setTimeout, setInterval, setImmediate, I/O,process.nextTick,Promise等),node会交给libuv库处理。
  3. libuv通过阻塞操作和多线程实现了异步io,也就是说libuv内部是完全异步的,这就是node.js的最大特点事件驱动的核心。
  4. 通过事件驱动的方式(Callback函数),将结果放到事件队列中,最终交给我们的应用。

宏任务和微任务

任务队列可分为宏任务和微任务

macro-task(宏任务): script(全局任务), setTimeout, setInterval, setImmediate, I/O, UI rendering。

micro-task(微任务): process.nextTick, 原生Promise(有些实现的promise将then方法放到了宏任务中),Object.observe(已废弃), MutationObserver

Javascript Event-loop的化繁为简(二)