Node.js 原理简介

Node.js 的官方文档中有一段对 Node.js 的简介,以下。html

Node.js® is a JavaScript runtime built on Chrome's V8 JavaScript engine. Node.js uses an event-driven, non-blocking I/O model that makes it lightweight and efficient.node

大意就是说 Node.js 是基于 V8 的 JavaScript 运行时,事件驱动、非阻塞,所以轻量、高效。git

寥寥数语,并无说清楚 Node.js 究竟是什么。参考了一些 Node.js 的官方文章以及社区里的分析,整理以下。github

基础架构

要想深刻理解 Node.js,咱们须要把 Node.js 进行必要的拆解,了解每一个组成部分的做用,它们之间如何交互,最终构成 Node.js 这个强大的运行时环境。npm

image

上图是 Node.js 的内部结构图。咱们能够看到,自底向上主要能够分红三层:最底层是 Node.js 依赖的各类库,有 V八、libuv 等;中间层是各类 Binding,也就是胶水代码;最上层是应用代码,可以使用 Node.js 的各类 API。promise

  • V8
    Google 开源的高性能 JavaScript 引擎,它将 JavaScript 代码转换成机器码,而后执行,所以速度很是快。V8 以 C++ 语言开发,Google 的 Chrome 浏览器正是使用的 V8 引擎。浏览器

  • libuv
    libuv 以 C 语言开发,内部管理着一个线程池。在此基础之上,提供事件循环(Event Loop)、异步网络 I/O、文件系统 I/O等能力。网络

  • 其余底层依赖库
    c-arescrypto (OpenSSL)http-parser 以及 zlib。这些依赖提供了对系统底层功能的访问,包括网络、压缩、加密等。架构

Node.js 底层的依赖库,有的以 C 语言开发,有的以 C++ 语言开发,如何让应用代码(JavaScript)可以与这些底层库相互调用呢?这就须要中间层的 Binding 来完成。Binding 是一些胶水代码,可以把不一样语言绑定在一块儿使其可以互相沟通。在 Node.js 中,binding 所作的就是把 Node.js 那些用 C/C++ 写的库接口暴露给 JS 环境。异步

中间层中,除了 Binding,还有 Addon。Binding 仅桥接 Node.js 核心库的一些依赖,若是你想在应用程序中包含其余第三方或者你本身的 C/C++ 库的话,须要本身完成这部分胶水代码。你写的这部分胶水代码就称为 Addon。本质上都是完成桥接的做用,使得应用与底层库可以互通有无。

应用层的代码,就没必要多言了,咱们开发的应用、npm 安装的包都运行在这里。

事件循环 (event loop)

刚接触 Node.js 的时候,就知道 Node.js 有一个事件循环,相似于 while(true),可是不知道每次循环何时开始,何时结束,在每次循环中,Node.js 是如何处理同步与异步代码的。

要说事件循环,就不得不先说明一下 Node.js 的工做流程。下图能够简要说明。

QRePV

一个 Node.js 应用启动时,V8 引擎会执行你写的应用代码,保持一份观察者(注册在事件上的回调函数)列表。当事件发生时,它的回调函数会被加进一个事件队列。只要这个队列还有等待执行的回调函数,事件循环就会持续把回调函数从队列中拿出并执行。

在回调函数执行过程当中,全部的 I/O 请求都会转发给工做线程处理。libuv 维持着一个线程池,包含四个工做线程(默认值,可配置)。文件系统 I/O 请求和 DNS 相关请求都会放进这个线程池处理;其余的请求,如网络、平台特性相关的请求会分发给相应的系统处理单元进行处理。

安排给线程池的这些 I/O 操做由 Node.js 的底层库执行,完成以后触发相应事件,对应的事件回调函数会被放入事件队列,等待执行后续操做。这就是一个事件在 Node.js 中执行的整个生命周期。

前面说了,咱们只知道 Node.js 有事件循环,可是不知道每次循环什么时候开始、什么时候结束。下面就简要说明一下每次循环的处理过程,详细内容请参考Node.js 官方说明

一次事件循环,大概能够分为以下几个阶段:
image

图中每个方块,在事件循环中被称为一个阶段(phase)。

每一个阶段都有本身独有的一个用于执行回调函数的 FIFO 队列。当事件循环进入一个指定阶段时,会执行队列中的回调函数,当队列中已经被清空或者执行的回调函数个数达到系统最大限制时,事件循环会进入下一个阶段。

上图中总共有6个阶段:

  • timers: 该阶段执行由 setTimeout()setInterval() 设置的回调函数。
  • I/O callbacks: 执行除了close 回调、timers 以及
    setImmediate() 设置的回调之外的几乎全部的回调。
  • idle,prepare: 仅供内部使用。
  • poll: 检索新的 I/O 事件;在适当的时候 Node.js 会阻塞等待。
  • check: 执行 setImmediate() 设置的回调。
  • close callbacks: 执行关闭回调。好比: socket.on('close', ...).

这里有个使人困惑的地方,I/O callbackspoll 这两个阶段有什么区别? 既然 I/O callbacks 中已经把回调都执行完了,还要 poll 作什么?

查阅了libuv 的文档后发现,在 libuv 的 event loop 中,I/O callbacks 阶段会执行 Pending callbacks。绝大多数状况下,在 poll 阶段,全部的 I/O 回调都已经被执行。可是,在某些状况下,有一些回调会被延迟到下一次循环执行。也就是说,在 I/O callbacks 阶段执行的回调函数,是上一次事件循环中被延迟执行的回调函数。

还须要提到的一点是 process.nextTick()process.nextTick() 产生的回调函数保存在一个叫作 nextTickQueue 的队列中,不在上面任何一个阶段的队列里面。当当前操做完成后,nextTickQueue 中的回调函数会当即被执行,无论事件循环处在哪一个阶段。也就是说,在 nextTickQueue 中的回调函数被执行完毕以前,事件循环不会往前推动。

测试与实践

以下代码中使用了 setTimeout(), setInterval(), setImmediate(), promise, process.nextTick(),可借助于输出结果,理解事件循环。

'use strict';

const fs = require('fs');

console.log('script start');

const interval = setInterval(() => {  
  console.log('setInterval')
}, 500);

setTimeout(() => {  
  console.log('setTimeout 1');
  Promise.resolve().then(() => {
    console.log('promise 3');
  }).then(() => {
    console.log('promise 4');
    process.nextTick(() => {
      console.log('nextTick 1');
    });
  }).then(() => {
    setTimeout(() => {
      console.log('setTimeout 2');
      Promise.resolve().then(() => {
        console.log('promise 5');
      }).then(() => {
        console.log('promise 6');
        process.nextTick(() => {
          console.log('nextTick 2');
        });
      }).then(() => {
        clearInterval(interval);
      });
    }, 0);
  });
}, 1000);

Promise.resolve().then(() => {  
  console.log('promise 1');
}).then(() => {
  console.log('promise 2');
});

setImmediate(() => {
  console.log('setImmediate 1');
});

console.log('script done');

执行结果为:

script start
script done
promise 1
promise 2
setImmediate 1
setInterval
setTimeout 1
promise 3
promise 4
nextTick 1
setInterval
setTimeout 2
promise 5
promise 6
nextTick 2
相关文章
相关标签/搜索