1.回调函数
Node.js是单进程,单线程的应用程序,而通过V8引擎提供的异步执行回调接口可以处理大量的并发请求,所以性能非常高.
Node.js几乎每个API都支持回调函数,用来维护并发.一边读取文件,一边执行其他的任务,读取完文件后,将文件的内容作为回调函数的参数进行返回,这样执行代码时就没有阻塞或等待I/O操作.
异步编程依托于回调函数来实现,但不意味着使用回调就异步化了.回调函数一般作为函数的最后一个参数出现,在回调函数内部,第一个参数为err用来接收代码中的错误,第二个参数接收真正返回结果的数据.
function 函数名 (arg1,arg2,callback(err,result){}){}
实例:回调函数读取文本文件
input.txt内容:
main.js
因为是非阻塞,程序的执行不用按照顺序执行.
1.1.阻塞
Node.js保留了JavaScript在浏览器中单线程的特点,单线程在程序执行时,程序按照连续的顺序依次执行.在Node.js标准库中一些I/O方法提供阻塞版本,名字通常以Sync结尾.
当我们执行程序时,对话框未关闭时,控制台没有输出,关闭提示框,控制台输出.
1.2.非阻塞
阻塞是按顺序执行的,非阻塞是不需要按照顺序执行的.在Node.js标准库中,所以的I/O方法都提供异步模式.异步模式在执行是非阻塞的.就像上面的例子.
2.异步编程
目前广泛用于处理异步编程的方法有三种:事件发布/订阅模式,Promise/Deferred模式和流程控制库.
2.1 事件发布/订阅模式
在该事件模式下,一个事件对象用来发布事件,即为发布者.订阅发布者所发布的事件,即为订阅者.但发布者发布一个事件时,发布者会通知所有订阅了该事件的订阅者.
2.1.1 事件驱动
Node.js所有的异步I/O操作完成后,都会发送一个事件到事件队列.Node.js使用事件驱动模型,当Web服务器收到请求时,先将他关闭,再进行处理,再去服务下个Web请求.在事件驱动模型中,会生成一个主循环来监听事件,当检测到事件后,会触发回调函数.流程图如下
2.1.2 EventEmitter类
Node.js有多个内置的事件,可以通过引入Events模块,并通过EventEmitter类来绑定和监听事件.
首先通过require加载Events模块,实例化一个对象
EventEmitter提供了多个方法,如on(),emit().on()用于绑定事件函数,emit()用于一个事件触发.
方法 | 功能 |
addListener(event,listener) | 指定事件添加一个监听器 |
on(event,listener) | 为指定事件注册一个监听器,接收一个字符串event和一个回调函数 |
emit(event,[arg1],[arg2],[...]) | 按监听器的顺序执行每个监听器,如果事件有注册监听,则返回去true,否则返false |
代码分析
首先加载核心模块events,在实例化一个对象events.EventEmitter,在订阅事件.on()方法用来注册事件event的一个监听器,emit()方法提交事件执行监听器,此时输出A.addListener()为指定事件click的一个监听器,事件提交后,会调用该监听器,在控制台输出B
2.2 Promise/Deferred模式
JavaScript的异步编程需要回调函数的支持,但是当业务逻辑很复杂时,回调的多层嵌套会让复杂度飙升,可读性会大大降低,维护也会变得困难,这就是"回调地狱".
Promise是异步编程问题的一种解决方案.Promise可以将异步的操作以同步的操作流程表达出来,避免了层层嵌套,解决了"回调地狱"问题.在开发时须提供Promise(承诺)对象所需的业务处理函数,在Deferred(延迟对象)中将事件处理函数进行绑定,先执行异步调用,然后延迟传递处理对象.
Promise通常用于外部,代表一个异步操作,Promise对象有三种状态:Penging(进行中),Fulfilled(已完成),Rejected(失败)状态一旦被改变,不可以更改.
Deferred主要用于内部,以维护异步模型的状态.通过resolve()方法,改变自身状态为执行态.并触发then()方法的onfuilled回调函数.通过reject()方法改变自身状态为拒绝态,并触发then()方法的onrejected回调函数.
示例:
2.3 流程控制库
在业务逻辑非常复杂时,异步嵌套逐渐增多,从而形成"异步地狱".对业务的逻辑和代码的维护造成困扰,处理异常也非常的繁琐.流程控制库主要用来控制多个异步回调时的顺序以及依赖.
async是node.js中流程控制模块,也可以在浏览器端使用.await用于等待一个异步方法执行完成.通过await处理async计算结果和错误.await只允许放在async()函数内部使用,它可以按顺序处理多个异步返回值.
原生方法读取多个文件:
async和await方法