阻塞
运行下列 dome 时,必须要一条一条的顺次打印出来。
换句话说,我在执行方法时必须要等待上一条语句执行完成后才能执行,因此阻塞了线程。
// app.js
console.log('hello World!');
console.log('Jiaiyan');
console.log('hello Jiaiyan!');
非阻塞
Node 使用了事件轮询,因此在这 setTimeout 时非阻塞的。
换句话来说,dome 运行到 setTimeout 时,后面的 console.log() 语句被立即执行,无需等待上一条语句是否执行完成。
// app.js
console.log('hello World!');
setTimeout(() => {
console.log('Jiaiyan');
}, 1000);
console.log('hello Jiaiyan!');
事件轮询?
从本质上来解释,就是 Node 会先注册事件,然后不停的询问内核这些事件是否已经分发。当事件分发时,对应的回调函数就会被触发,然后继续执行下去。如果没有事件出发,则继续执行其他代码,直到有新事件时,再去执行对应的回调函数(setTimeout 仅仅是注册了一个事件,然后让程序继续执行,所以,这是异步的)。
Node.js 单线程的世界
众所周知,Node.js不借助任何模块是以单线程的模式运行着的。
通过下述带么可以很好的验证这一点,以及展示它和事件轮询之间的关系:
// app.js
var start = Date.now();
setTimeout(() => {
console.log(Date.now() - start);
}, 1000);
setTimeout(() => {
console.log(Date.now() - start);
}, 2000);
下面是我电脑打印出来的(单位是毫秒):

输出结果,明显与之前设置的不符,产生这个问题的原因,是因为事件轮询被 JavaScript 代码阻塞了。当第一个事件分发时,会执行 JavaScript 回调函数。由于回调函数执行需要一定的时间,所以下一轮的事件轮询事件就超过了设定时间。因此, JavaScript 中的 setTimeout 并不能严格的遵守时钟设置。
如果按照事件轮询所述,正在执行任务只有一个线程,也就是说,当一个函数执行时,同一时间 不可能有第二个函数在执行,并且每一个函数都有事件延迟,相当于同一时间段内参数执行量变少,这样不更影响效率吗?Node 是如何做到高并发处理的呢?
堆栈
先看一段代码:
http.createServer(function() {
a();
});
function a() {
b();
}
function b() {}
V8 在执行第一个函数时,会创建一个公共调用堆栈。如果该函数调用另一个函数,V8 就会把它添加到堆栈上去。上述代码,一旦 Http请求到达服务,Node 会先分发一个通知。然后回调函数会被执行,并且调用堆栈会变为 a => b。
由于 Node 是运行在单线程的环境中,那么,当调用堆栈展开时,Node 就无法处理其他的请求了,那么并发量不久是为 1 了吗?是的,Node 并不是提供正真的并行操作,因为那样就需要引入更多的并行线程(后期会与大家一起学习 Node 多线程)。其实,在调用堆栈非常快的情况下你无需处理多个请求。这也就是为什说V8 和 非阻塞IO 是最好的搭配:V8 的运行速度非常快,非阻塞IO 确保了单线程在运行时,不会受到数据库访问与硬盘操作而导致挂起的事情了。
网友评论