随着
JS在服务端的发展(例如Node),在处理数据库事务,操作文件等需要异步操作的地方,回调函数的缺点随着异步操作的复杂越来越明显,使用回调函数也越来越难以满足开发需要。为了解决回调函数的种种缺点,使包含异步操作的代码更简洁和具有可读性,ES6提出了
Promise的实现。
回调函数
为了不在进行表单检查,操作DOM等情况时阻塞页面,JS包含了大量的异步操作,随之诞生的就是回调函数。
回调函数是JavaScript中约定的一个俗称,指那些需要异步操作完成后才会进行调用的函数。主要用在那些需要花费时间,但又不能一直阻塞代码执行的情况,例如Ajax 和 File操作。
我们常见的jQuery库中的ajax相关API就是回调函数应用的一个比较显著的例子
1  | $.get('ajax/test.html',function(data){  | 
但是回调函数也存在以下的一些缺点:
- 著名的 callback hell 回调地狱,在进行多个顺序依赖的异步操作时,很多个回调函数,一层层嵌套,导致代码可读性极低,不利于阅读和维护。
 - 调用函数和回调函数并不会在同一个堆栈中运行,这会导致我们无法对回调函数内部发生的异常进行
try-catch和准确定位,无法使用throw抛出异常,无法使用return终止函数的调用等问题。 - 回调函数使用了
JS闭包,在比较复杂的项目环境时,容易出现变量污染等难以定位和调试的问题 
正是由于回调函数的这些缺点,Promise应运而生。
promise的创建和使用
在ES6中被实现的Promise,主要用途就是进行异步计算,通过将异步操作队列化,使其按我们期望的顺序执行,并返回我们期望的结果。
创建promise对象
我们可以通过Promise这个原生构造函数来创建一个promise。
1  | var promise = new Promise(function(resolve,reject){  | 
构造函数Promise接受一个函数作为参数,这个函数有两个参数resolve和reject(可省略),这两个参数都是Promise对象的方法。
使用new Promise() 创建的promise对象有三个状态:
- Pending 
promise对象刚被创建后的初始状态 - Fullfilled 异步操作成功状态,可使用
resolve方法将promise置为此状态并将异步操作的结果传递出去 - Rejected 异步操作失败状态,可使用
reject方法将promise置为此状态并将异步操作的错误传递出去 
promise的状态改变是不可逆的,也就是说,从初始化状态 Pending 转换为 Fullfilled 或 Rejected 后,promise的状态就不会再改变了。
promise.then()
在Promise对象的的创建中我们提到通过resolve方法传递结果和通过reject方法传递错误,那想要接收这些结果和错误,就需要用到promise对象的then()方法了:
1  | promise.then(onFulfilled,onRejected)  | 
.then接受两个函数作为参数。这两个函数参数都是可选的。
onFulfilled在promise对象通过resolve将状态置为Fulfilled时会被调用,接受resolve传递过来的结果作为函数参数。onRejected在promise对象通过reject将状态置为Rejected时会被调用,接受reject传递过来的错误作为函数参数。
.then的参数函数的显式返回值会被包装为一个同样具有.then方法的新的promise对象,下一个.then 的onFulfilled和onRejected针对这个promise对象遵循相同的调用方式,从而实现.then的链式调用。
使用
.then来进行异常处理时,可使用promise.then(undefine,onRejected),只指定reject时的处理函数。但其实更好的办法是使用
promise.catch(onRejected)这个专门用于处理异常的promise方法。它的优势在于在链式调用.then时,它可以捕捉到前面所有then中传递出的异常。但要注意的是,除非
.then()和.catch()内部抛出异常或使用了rejecte()将状态置为rejected,他们都会返回Fulfilled态的promise对象。
一个Promise的基本例子
我们通过使用promise来实现一个类似jQuery$.get()的函数,从而对以上介绍能有更直观的认识:
1  | function getUrl(url){  | 
小结
通过以上的介绍和代码,我们就知道了promise的基本使用方法:
- 使用 
new Promise创建promise对象,使用resolve和reject传递异步结果。 - 使用 
.then或.catch对promise对象传递出的结果进行处理。 
Promise的静态方法
Promise 提供了几个静态方法,用于辅助我们使用Promise。
Promise.resolve()
Promise.resolve()也是用于创建一个promise对象,可以认为是如下形式的new Promise():
1  | Promise.resolve('200')  | 
对于Promise.resolve(),它总是返回一个promise对象,并且会将promise对象立刻置为resolved状态(除非解析发生错误或传入了状态为rejected的promise对象),进而触发后续then()中的onFulfilled函数。
有以下几点需要注意:
- 如果传递给
Promise.resolve()的参数为一个直接的值,它会把它包装为promise对象返回,下一个.then的onfulfilled函数会立即获得这个值,但仍然是异步的。 - 如果传递给 
Promise.resolve()的参数为一个promise对象,它不会做任何处理,而是直接返回传入的promise对象。 - 如果传递给
Promise.resolve()的参数为一个具有.then方法的对象,它会将其包装为一个promise对象返回,并立即执行它的then方法。 Promise.then()中的onFulfilled函数的显式返回值,即是通过Promise.resolve()包装为promise对象供后续的then使用的。
Promise.reject()
与Promise.resolve()类似,Promise.rejct()也是用于创建一个promise对象,可以认为是如下形式的new Promise():
1  | Promise.reject(new Error('错误'))  | 
对于Promise.reject(),它总是返回一个promise对象,并且会将这个promise对象立刻置为rejected状态,进而触发后续then()中的onRejected函数。
Promise.all()
Promise.all()接受一个promise对象的数组作为参数,它会将数组中所有promise对象实例包装为一个新的promise对象。
这个新promise对象的状态由数组中所有promise对象的状态来决定。当数组中所有对象都resolved时,它也会转换为resolved状态。当数组中有一个对象转换为rejected状态时,它就会转换为rejected状态。
这个新promise对象的结果为传入数组中各个promise的结果组成的一个数组。
1  | var arr = [1, 2, 3];  | 
需要注意的是,
Promise.all()参数数组中所有promise对象包裹的异步操作都是并发执行的,他们的结果互不干扰互不依赖。如果想实现队列型的异步操作,应该使用链式调用
.then()的方式来实现。
Promise.race()
Promise.race() 和 Promise.all()一样,也是接受一个promise对象的数组,将数组中的所有promise对象包装为一个新的promise对象。
但不同的是,这个新promise对象的状态由数组中率先发生状态变化的promise对象来决定(race 也就是赛跑的意思)。当数组中第一个发生状态变化的对象转换为resolved时,它也会转换为resolved状态。当数组中第一个发生状态变化的对象转换为rejected状态时,它也会转换为rejected状态。
1  | var p1 = new Promise((resolve)=>{  | 
需要注意的是,
Promise.race在第一个promise对象改变状态之后,是不会去取消其他promise对象的执行的。
总结
以上就是关于ES6中Promise的一些知识,在看完这篇文章之后,大家应该对Promise有了比较清晰的了解和认识。
但本人才疏学浅,错误和遗漏在所难免。如果想要进一步深入的学习Promise,为大家提供以下两个文章链接,供大家继续深入的了解和学习promise的相关知识:
