首先明确一个问题,为什么 Node.js 需要异步编程?
JavaScript 是单线程的,在发出一个调用时,在没有得到结果之前,该调用就不返回,意思就是调用者主动等待调用结果,换句话说,就是必须等待上一个任务执行完才能执行下一个任务,这种执行模式叫:同步。
Node.js 的主要应用场景是处理高并发(单位时间内极大的访问量)和 I/O 密集场景(ps: I/O 操作往往非常耗时,所以异步的关键在于解决 I/O 耗时问题),如果采用同步编程,问题就来了,服务器处理一个 I/O 请求需要大量的时间,后面的请求都将排队,造成浏览器端的卡顿。异步编程能解决这个问题。
所谓异步,就是调用在发出后,这个调用就直接返回了,调用者不会立即得到结果,但是不会阻塞,可以继续执行后续操作,而被调用者执行得到结果后通过状态、事件来通知调用者使用回调函数( callback)来处理这个结果。Node在处理耗时的 I/O 操作时,将其交给其他线程处理,自己继续处理其他访问请求,当 I/O 操作处理好后就会通过事件通知 Node 用回调做后续处理。
有个例子非常好:
你打电话问书店老板有没有《分布式系统》这本书,如果是同步通信机制,书店老板会说,你稍等,”我查一下",然后开始查啊查,等查好了(可能是5秒,也可能是一天)告诉你结果(返回结果)。而异步通信机制,书店老板直接告诉你我查一下啊,查好了打电话给你,然后直接挂电话了(不返回结果)。然后查好了,他会主动打电话给你。在这里老板通过“回电”这种方式来回调。
下面几种方式是异步解决方案的进化过程:
CallBacks
回调函数就是函数A作为参数传递给函数B,并且在未来某一个时间被调用。callback的异步模式最大的问题就是,理解困难加回调地狱(callback hell),看下面的代码的执行顺序:
A();
ajax('url1', function(){
B();
ajax('url2', function(){
C();
}
D();
});
E();
其执行顺序为:A => E => B => D => C,这种执行顺序的确会让人头脑发昏,另外由于由于多个异步操作之间往往会耦合,只要中间一个操作需要修改,那么它的上层回调函数和下层回调函数都可能要修改,这就陷入了回调地狱。而 Promise 对象就很好的解决了异步操作之间的耦合问题,让我们可以用同步编程的方式去写异步操作。
Promise
Promise 对象是一个构造函数,用来生成promise实例。Promise 代表一个异步操作,有三种状态:pending,resolved(异步操作成功由 pending变为 resolved),rej