nodejs的主要特点是单线程、异步I/O、事件驱动。让我们先大概了解一下这些名词的意思。
单线程
单线程是任务按照顺序执行的,并且每次只执行一个任务,只有前面的任务执行完成以后,后面的任务才执行。在JS引擎中负责解释和执行JavaScript代码的线程只有一个,即主线程。但实际上还存在其他的线程。例如处理AJAX请求的线程、处理DOM事件的线程、定时器线程、读写文件的线程等。这些线程可能存在与JS引擎之内,也可能存在与JS引擎之外,这些线程为工作线程。
同步和异步
同步:执行任务时,后面的任务需要等到前一个任务执行结束才可以执行,任务执行的顺序和任务排列的顺序是相同的
异步:每个任务有一个或多个callback,前一个任务执行完成以后不是执行后一个任务,而是执行回调函数。后一个任务也不是等到前一个任务执行完成后再执行,程序的执行顺序与任务的排列顺序不一致
现在我们看一下异步调用的基本过程
var fs = require('fs');
console.log('begin');
setTimeout(function(){
console.log('timeout1');
},100) fs.readFile('test.txt',function(err,data){
console.log(data);
console.log(data.toString());
}) setTimeout(function(){
console.log('timeout2');
},100) console.log('end');
分析一下结果:
由于NodeJS是单线程运行的,首先会顺序执行js文件中的代码,此时事件循环是暂停的。setTimeout和读文件的操作都是异步操作,异步函数会在工作线程执行,当异步函数执行完成以后,将回调函数放入消息队列。当js文件执行完成以后,事件循环开始执行,并从消息队列中取出消息,开始执行回调函数。
而整个异步函数的执行过程如下:
主线程发起一个异步请求,相应的工作线程接收请求并告知主线程已收到(异步函数返回);主线程可以继续执行后面的代码,同时工作线程执行异步任务;工作线程完成工作以后,通知主线程;主线程收到通知后,执行一定的动作(调用回调)
事件驱动
事件驱动编程主要思想是通过事件或状态的变化来进行应用程序的流程控制,一般通过事件监听完成,一旦事件被检测到,则调用相应的回调函数。事件驱动主要执行过程是当进来的一个新的请求的时候,请求将会被压入队列中,然后通过一个循环来检测队列中的事件状态变化,如果检测到有状态变化的事件,那么就执行该事件对应的处理代码,一般都是回调函数。
线程驱动是当收到一个请求的时候,将会为该请求开一个新的线程来处理请求。而线程主要是由线程池来管理的。当线程池中有空闲的线程,会从线程池中拿取线程来处理,如果线程池中没有空闲的线程,新来的请求将会进入队列排队,直到线程池中空闲线程
异步I/O
nodejs是单线程运行的,通过一个事件循环来循环取出消息队列中的消息进行处理,处理过程基本上就是去调用该消息对应的回调函数。消息队列就是当一个事件状态发生变化时,就将一个消息压入队列中
NodeJS的事件循环模型一般要注意下面几点:
- 因为是单线程的,所以当顺序执行js文件中的代码的时候,事件循环是被暂停的
- 当JS文件执行完以后,事件循环开始运行,并从消息队列中取出消息,开始执行回调函数
- 因为是单线程的,所以当回调函数被执行的时候,事件循环是被暂停的
- 当涉及到I/O的时候,nodejs会开一个独立的线程来进行异步I/O操作,操作结果以后将消息压入消息队列
内部实现
在NodeJS中整个异步I/O模型的基本要素是事件循环、观察者、请求对象和I/O线程池。整个异步I/O的流程图如下:
整个实现过程大概更可以描述为:发起异步请求之后将请求进行封装,封装为请求对象,对请求对象设置参数和回调函数并将请求对象放入线程池,线程池中检查是否有可用线程,当线程可用时执行请求对象的I/O操作,并将执行完成的结果放入请求对象中,通知IOCP调用完成并获取完成的I/O交给I/O观察者。
在libuv中创建主循环开始事件循环,主循环从I/O观察者中取出可用的请求对象,在请求对象中取出回调函数和I/O结果并调用回调函数
https://segmentfault.com/a/1190000003751278
转载至 :http://blog.csdn.net/charlene0824/article/details/51711154