异步编程——Promise
之前我们说到,当通过回调函数执行多步异步操作时,会创造难以理解和调试的代码,而且也缺乏控制异步操作时机的手段。比如想让两个异步操作同时运行,或者同时启动多个异步操作只采用某个结果,都很难做到。在这些情况下,通过使用ES6的新特性——Promise将能大幅度改善这种情况。
什么是Promise?
我们都知道,之前在构建异步事件处理函数时,都是通过传入回调函数的手段将事件和方法耦合在一起在实现的。这也是多个异步操作串联时难以操作的原因。而Promise就相当于事件和方法的中转站,将事件和方法解耦,从而使整个异步过程变得清晰。
Promise就是为异步事件的结果准备的占位符,该占位符承接异步事件的状态,根据不同状态来开启针对异步事件的方法的执行。Promise通过它的生命周期来承接异步事件的状态。
Promise的生命周期
Promise内部存在[[PromiseState]]属性,该属性有'pending','fulfilled','rejected'来反映异步事件的状态。
- pending(挂起态): 异步事件尚未结束。
- fulfilled(已完成): 异步事件已成功结束。
- rejected(已拒绝): 异步事件未成功结束,可能发生了一个错误。
该属性会随着异步事件的发生流程而改变,我们通过then()方法监听该属性,来开启针对不同流程所执行的方法。Promise初始为挂起态,只可以单向转换为已完成或已拒绝。无论转化为哪种状态,Promise都可以称为已决态。
至此我们就实现了一个异步事件处理流程,也完成了事件与方法的分离。
Promise的创建
Promise通过Promise构造器来创建。该构造器接受单个参数:一个被称为执行器(executor)的函数,该函数包含初始化Promise的代码,并且被传递两个名为resolve()与reject()的函数作为参数。reslove()在执行器结束后被调用,表示进入fulfilled状态。而当执行器操作失败时,reject()被调用,表示进入rejected状态。我们通过then()方法监听Promise的状态,第一个参数为fulfilled状态调用的函数,第二个参数为rejected状态调用的函数。
下面以一个读取文件的异步操作例子来解释整个流程:
let fs = require("fs");
let promise = new Promise(function(resolve,reject){
//触发执行器异步事件
fs.readFile(filename,{encoding: "utf-8"},function(err,contents){
if(err){
//执行器操作失败,进入rejected状态
reject(err);
return
}
//执行器操作成功,进入fulfilled状态
resolve(contents);
});
});
//分别监听两个状态并输出执行器返回值
promise.then(function(contents){
console.log(contents);
},function(err){
console.error(err.message);
});
在Promise中执行器会立刻执行,resolve()或reject()的完成会作为异步事件操作结束的标志,转换Promise状态并添加进事件循环队列,以便then函数进行后续监听。
then()方法在所有Promise上都存在,传递给then的两个参数都可选,因此可以进行各种形式与数量的监听与拒绝函数。
当只监听rejected状态时,可以使用更简单的catch()方法。
已决的Promise的创建
有时候我们可能并不需要创建复杂的异步操作,只想让Promise代表一个已知的值。那么我们可以用以下两种方法创建已决的Promise。
Promise.resolve()
该方法接受单个参数并返回一个fulfilled状态的Promise。我们可以通过then方法的第一个函数参数接收该值并处理。
Promise.reject()
该方法接受单个参数并返回一个rejected状态的Promise。我们可以通过then方法的第二个函数参数接收该值并处理。
Promise的串联操作
目前我们了解了单个Promise的处理流程,而只对一个异步事件进行处理的话用回调函数其实就足够了。Promise拥有同时处理多个异步事件串联的能力,这正是回调函数难以做到的。其关键在于then()或catch()方法调用时实际上创建并返回了另一个未决的Promise。当前一个Promise已决时,才能进行后续的决议。
之前的流程我们可以看出,传递给执行器中的resolve()的参数会被传递给Promise的then()对应的函数参数。而当Promise串联时,对应函数参数的返回值也会继续向下传递给Promise对象的then()对应函数参数,返回值也可以是一个新建的Promise对象。reject()函数也是同理。
多个Promise的响应
有时我们可能想同时监视多个异步操作,以便根据总体情况决定接下来的行动。ES6提供了两个方法:
Promise.all()
该方法接受单个可迭代对象作为参数并返回一个Promise。这个参数元素都是Promise,只有在它们都被决议后,返回的Promise才会被决议。如果所有参数Promise都是fulfilled状态,则返回的Promise也是fulfilled状态并接收一个包含每个决议值的数组。若任意参数Promise被拒绝,那么返回的Promise会立刻被拒绝。
Promise.race()
该方法接受参数同上,但返回的Promise状态如同该方法名,与最快转化为已决态的参数Promise相同。
-- EOF --