Node.js是什么
Node.js是一个平台,它将多种技术组合起来,让JavaScript也能调用系统接口、开发后端应用。 所以Node.js不是编程语言更不是web框架。
Node.js技术架构
bingings
bingings是用C++对http_parser等库进行封装,使它符合某些要求,可以使用Node.js提供的工具将其编译为.node文件,js代码可以直接require这个.node文件。这样js就能直接调用C++库,中间的桥梁就是binding。由于有很多个这样的桥梁,所以叫做bindings。
libuv
是一个跨平台的异步I/O库,开始写libuv。可以用于TCP/UDP/DNS/文件等的异步操作。
V8
V8是一个将js源代码变成机器码并执行的工具。它有以下功能:
- 维护调用栈,确保js函数的执行顺序;
- 内存管理,为所有对象分配内存;
- 垃圾回收,重复利用无用的内存;
- 实现js的标准库。比如浏览器的DOM,可以通过V8来执行。
特别注意
- V8不提供DOM API;
- v8执行js是单线程的;
- 可以开启两个线程分别执行不同的js文件;
- V8本身是包含多个线程的,比如垃圾回收就是一个单独的线程;
- 自带event loop,但是Node.js基于libuv自己实现了一个。
Node.js的event loop
JavaScript 是单线程的,有了 event loop 的加持,Node.js 才可以非阻塞地执行 I/O 操作,把这些操作尽量转移给操作系统来执行。event loop就是对事件处理顺序的管理。
其中每个方框都是 event loop 中的一个阶段。 每个阶段都有一个「先入先出队列」,这个队列存有要执行的回调函数。
什么时候停止执行这些回调呢?下列两种情况之一会停止:
- 队列的操作全被执行完了;
- 执行的回调数目到达指定的最大值 然后,event loop 进入下一个阶段,然后再下一个阶段。
各阶段概览
- timers 阶段:这个阶段执行 setTimeout 和 setInterval 的回调函数;
- I/O callbacks 阶段:不在 timers 阶段、close callbacks 阶段和 check 阶段这三个阶段执行的回调,都由此阶段负责,这几乎包含了所有回调函数。这个阶段会执行一些系统操作的回调函数,比如 TCP 报错,如果一个 TCP socket 开始连接时出现了 ECONNREFUSED 错误,一些 *nix 系统就会(向 Node.js)通知这个错误。这个通知就会被放入 I/O callbacks 队列。
- idle, prepare 阶段:event loop 内部使用的阶段(我们不用关心这个阶段)
- poll 阶段:获取新的 I/O 事件。在某些场景下 Node.js 会阻塞在这个阶段。
- check 阶段:执行 setImmediate() 的回调函数。
- close callbacks 阶段:执行关闭事件的回调函数,如 socket.on('close', fn) 里的 fn。
setImmediate() vs setTimeout() setImmediate 和 setTimeout 很相似,但是其回调函数的调用时机却不一样。
setImmediate() 的作用是在当前 poll 阶段结束后调用一个函数。 setTimeout() 的作用是在一段时间后调用一个函数。 这两者的回调的执行顺序取决于 setTimeout 和 setImmediate 被调用时的环境。
setImmediate 的主要优势就是,如果在 I/O 操作的回调里,setImmediate 的回调总是比 setTimeout 的回调先执行。
process.nextTick()
你可能发现 process.nextTick() 这个重要的异步 API 没有出现在任何一个阶段里,那是因为从技术上来讲 process.nextTick() 并不是 event loop 的一部分。实际上,不管 event loop 当前处于哪个阶段,nextTick 队列都是在当前阶段后就被执行了。所以process.nextTick() 比 setImmediate() 更 immediate(立即)一些。
Node.js工作流程:
其中黄色标识是经常用到的API,我们在以后的Node.js文章中再介绍。