Node.js 回调函数和事件循环

1. node.js 回调函数

node.js 的异步编程思想最直接的体现就是回调,在node中大量使用了回调函数,所有的API都支持回调函数,回调函数一般作为最后一个参数出现,正因为这样node在执行代码的时候就没有阻塞或者等待的操作,提高了node的性能,可以处理大量的并发请求。

function f1(name, age, callback){}

function f2(name, callback, callback2){}

阻塞代码实例

创建一个文件input.txt内容如下:

这是一个阻塞代码的实例

创建 node.js:

var  fs = require('fs');
var  data = fs.readFileSync('input.txt')

console.log(data.toString())

console.log('程序执行结束!')

结果如下:

这是一个阻塞代码的实例
程序执行结束!

如上:阻塞代码就是需要等待前面的代码执行完成后才能继续往后执行。

非阻塞代码实例

创建一个文件input.txt内容如下:

这是一个非阻塞代码的实例

创建 node.js:

var fs = require('fs')

fs.readFile('input.txt', function(err, data){
    if (err) return console.log(err)
    console.log(data.toString())
})

console.log('over')

结果如下:

over
这是一个阻塞代码的实例

如上,就是程序不必等到读取操作而直接执行后面的代码,等到读取完成后在执行读取文档的相关操作。

总结

阻塞是按顺序执行的,而非阻塞是不需要按照顺序的,需要处理的事件就写在回调函数之内即可。

node.js 事件循环

node.js 是单进程单线程应用程序,但是因为V8引擎提供的异步执行回调接口,通过这些接口可以处理大量并发,所以性能非常高,在nodejs中所有的事件机制都是用设计模式中观察者模式实现

node.js 单线程进入一个 while 的事件循环,知道没有事件观察者退出,每个异步事件都生成一个事件观察者,如果事件发生就调用该回调函数

node.js 事件驱动程序

node.js 使用事件驱动模型,当web server 接受到请求,就把它关闭然后处理,在去处理下一个web请求。当这个请求完成后,它会被放回到处理队列的开头,并将这个结果返回给用户。

node.js 的事件驱动扩展性非常强,因为web server一直在接受请求,而不进行任何等待操作,效率非常的高。整个流程类型观察者模式,事件相当于一个主题,所有注册到这个事件上的处理函数相当于观察者。

内置实例

// 引入 events 模块
var events = require('events');

// 创建 eventEmitter 对象
var eventEmitter = new events.EventEmitter();

绑定事件:

// 绑定事件及事件的处理程序
eventEmitter.on('eventName', eventHandler);

触发事件:

// 触发事件
eventEmitter.emit('eventName');

实例

// 引入 events 模块
var events = require('events');
// 创建 eventEmitter 对象
var eventEmitter = new events.EventEmitter();

// 创建事件处理程序
var connectHandler = function connected() {
   console.log('连接成功。');
  
   // 触发 data_received 事件 
   eventEmitter.emit('data_received');
}

// 绑定 connection 事件处理程序
eventEmitter.on('connection', connectHandler);
 
// 使用匿名函数绑定 data_received 事件
eventEmitter.on('data_received', function(){
   console.log('数据接收成功。');
});

// 触发 connection 事件 
eventEmitter.emit('connection');

console.log("程序执行完毕。");

执行结果:

连接成功
数据接受成功
程序执行完毕

node 应用程序如何工作

在 Node 应用程序中,执行异步操作的函数将回调函数作为最后一个参数, 回调函数接收错误对象作为第一个参数。

具体案例可以看上文的 非阻塞代码的实例

正常执行结果就如上

如果把input.txt删除,那么就导致程序读取错误,就会走到代码的 err部分,就会出现如下结果:

over
Error: ENOENT, open 'input.txt'