对于一些还不具有大量编程经验的朋友来讲,promise多是es6比较难以掌握的点。首先是不少名词,好比Promises,es6 Promise, 回调函数(callback),Promise/A+,异步编程等。下面就首先介绍下这些名词的含义和区别。es6
所谓异步编程中的异步是相对于同步的概念的。js是单线程的语言,同一时间只能作一件事,为了指定一些稍后要执行的代码,咱们须要异步。在客户端,主要的异步方式有事件,setTimeout,Ajax等。Node的发展大大扩展了js语言的边界,咱们知道,Node使用非阻塞IO模型,它使用回调函数模式来实现异步编程。好比:编程
readFile('example.txt', function(err, contents){ if(err){ throw err; } console.log(contents); }); console.log('Hi!');
上面代码中readFile的第二个参数就是回调函数。它会在读取完example.txt后被添加到执行队列中。上面代码的执行顺序是--执行readFile函数,在遇到读取文件时暂停,打印"Hi",读取文件结束后将回调添加到做业队列中,执行回调函数,打印contents。segmentfault
原本呢,使用回调函数是可以完成异步编程的。可是随着代码的逻辑越复杂,这种异步编程方式愈来愈难以阅读和追踪程序错误,因此发展出了Promises规范来完成异步编程。数组
Promises是一系列异步编程规范的统称。咱们须要了解的是其中的Promise/A+规范。es6经过Promise这个内建对象实现了该规范。因此咱们可使用es6中的Promise对象来进行异步编程。promise
下面将对es6中的Promise对象进行介绍。至于jQuery中延迟对象$.deferred(),根据规范本身实现promise和ES7的Async/Await异步方式等更多内容,后面会专门写一篇文章进行介绍。异步
一个promise实例有3种状态,分别是:异步编程
promise处于这3种状态中的一种,而且能够由pending状态变为fulfilled状态,或由pending变为rejected状态。反之则不行。函数
为了便于理解,下面将经过一个生活化的例子,来解释什么是Promise?学习
Promise是允诺的意思。它就是一个关于未发生的事情的承诺。好比:this
你订了一份烧烤,店家说半个小时内送到,这就是一个Promise。如今,这个Promise尚未发生,因此可能半个小时内配送成功或者失败。对此,你预备了两种处理方式:成功 -- 美滋滋的吃烧烤,失败 -- 去楼下店里吃。
在半个小时内,这个Promise处于pending状态,你正常上网,撸代码。一段时间后,这个promise就有告终果。是成功(fulfilled)或者失败(rejected)。根据这个结果,你以前的两种处理方式就会相应执行。这就是promise。
对应的代码以下:
let promise = new Promise(function(resolve, reject){ //等待店家送来中... let result = '配送成功'? true : false; if(result){ resolve(value); }else{ reject(reason); } }); promise.then(function(value){ //美滋滋吃烧烤... //value为上面resolve()中传递的值, 好比共100块钱。 }, function(reason){ //叫上隔壁老王去楼下吃... //reason为上面reject()的传递的缘由,好比烤糊了... });
上面代码就是经过promise异步编程的代码。这里要注意的是Promise构造函数接收一个函数做为参数,函数内部是异步的逻辑。这个函数接收两个参数:resolve和reject。resolve()能够把promise推向fulfilled状态,reject()能够把promise推向rejected状态。
promise有个then方法,用于处理promise成功或失败后的逻辑。then有两个参数:
参数1为promise成功时执行的函数,该函数的参数value对应于上面resolve(value)中的value值;
参数2为promise失败时执行的函数,该函数的参数reason对应于reject(reason)中的reason值,表示失败的缘由。
一旦promise有告终果(成功或失败),就会执行对应then中的函数。
Ajax是客户端最经常使用的异步编程场景,下面一个例子演示了使用Promise进行Ajax操做的代码。
function getData(method, url){ let promise = new Promise(function(resolve, reject){ let xmlHttp = new XMLHttpRequest(); xmlHttp.open(method, url); xmlHttp.send(); xmlHttp.onload = function () { if (this.status == 200 ) { resolve(this.response); } else { reject(this.statusText); } }; xmlHttp.onerror = function () { reject(this.statusText); }; }) return promise; } getData('get','www.xxx.com').then(successFun, failFun); function successFun(value){ //Ajax成功处理函数... } function failFun(reason){ //Ajax失败处理函数... }
前面的例子promise建立时,promise都处于pending状态,根据异步操做的结果将promise推向成功或失败状态。
Promise类型有两个静态方法Promise.resolve(value),Promise.reject(reason)能够分别建立已是fulfilled和已是rejected状态的promise。
好比:
let promise = Promise.resolve(44); promise.then(function(value){ console.log('fulfilled', value); })
上面代码promise在被建立出来时,已是fulfilled状态,接下来会直接将then中的回调函数加入到做业队列中,等待做业队列中前面的任务完成后执行该函数。
这里传入Promise.resolve(value)和Promise.reject(reason)中的参数和以前Promise构造是对应的参数是同样的。
上面已经演示过promise实例上then方法的用法,每个promise实例还具备catch方法。
catch()方法只处理reject的状况,他的行为与调用Promise.prototype.then(undefined, onRejected)相同。好比:
let p = new Promise(function(resolve, reject){ //... reject(new Error('something wrong!')) }) p.catch(function(reason){ //拒绝 })
上面catch方法中的回调在promise被reject时调用。
每次对then()或catch()的调用都会返回另外一个promise,这也是不少代码能够写成相似链式调用的缘由。好比:
let p1 = new Promise(function(resolve, reject){ resolve(42); }); let p2 = p1.then(function(value){ console.log(value); }) p2.then(function(){ console.log("Finished"); }, function(){ console.log('something wrong!'); }); p1 == p2 // false,注意:p1.then()会返回一个新的promise,因此p1与p2并不相等 //能够写成链式调用的形式,好比 p1.then(function(value){ console.log(value) }).then(function(){ console.log('do something'); }).then(function(){ console.log('Finished'); })
在上面代码中,p1.then()返回了一个promise为p2, 那么p2的状态和p1之间有什么关系呢?
更具体一点说,当p1变为fulfilled时,p1.then()返回的p2是什么状态呢?两者有什么联系呢?
p2的行为与p1.then()中回调函数的返回值有关:
Promise内建对象上的静态方法Promise.all()用于处理多个promise的状况。
Promise.all([promise1, promise2,...])返回一个promise的实例,接收一个promise组成的数组为参数。只有当数组内的promise都成功时,才会调用对应的then中的成功处理函数,只要有一个不成功,那么调用对应的拒绝处理函数。
依然使用前面那么订烧烤的例子,你不只订了烧烤,还在另外一家订了啤酒。打算等到烧烤和啤酒都配送成功后一块儿吃,美滋滋~~。好比:
Promise.all([订烧烤,订啤酒]).then(function(value){ //吃烧烤,喝啤酒... }, function(reason){ //拒绝的缘由,烤糊了或者啤酒卖完了... })
这里要注意的一点是,对于数组中的promise,只要有任一个promise为拒绝,那么就会当即执行then中的拒绝处理函数,并不会等待其余promise的结果。只有当全部promise的结果都成功时,才执行then中的成功处理函数。好比:
var p1 = new Promise(function(resolve, reject){ setTimeout(function(){ console.log('A'); resolve(); }, 1000) }); var p2 = Promise.reject(new Error('error')); var p3 = new Promise(function(resolve, reject){ setTimeout(function(){ console.log('B'); resolve(); }, 0) }); Promise.all([p1,p2,p3]).then(function(value){ console.log('success!'); }, function(reason){ console.log('failed'); }) //结果为failed B A
因为p2为已拒绝状态的promise,因此Promise.all()当即变为拒绝状态,打印failed,p1和p2会继续执行,但对于Promise.all()的结果没有影响。
Promise内建对象上的静态方法Promise.race()一样用于处理多个promise的状况。一样返回一个Promise,一样接收一个promise数组做为参数。
与all不一样的地方在于,数组中的promise就像在赛跑同样(race),而且只关心第一名的状况,只要有其中一个promise有告终果,Promise.race()的状态就会当即与该promise相同。
数组中其余promise继续执行,但对于Promise.race()的结果没有影响。
var promise = new Promise(function(resolve, reject){ console.log('A'); resolve('C'); }) console.log('B'); setTimeout(function(){ console.log('D'); },0) promise.then(function(value){ console.log(value) }); //打印A, B, C, D
p.then(function(value){ //... }) //或者 p.then(undefined, function(reason){ //... })
那么,相应的回调处理函数被忽略,then方法返回的promise会保留上一个promise的状态和参数。最典型的例子:
var p = new Promise(function(resolve, reject){ reject(new Error('error')); }) p.then(function(value){ //... }).then(function(value){ //... }).then(undefined, function(reason){ console.log(reason); }) //打印'error'
p的前两次then调用的拒绝处理函数被忽略,而后reject状态和错误信息就一直日后传递,直到被最后一次then调用捕获。
关于Promise的知识点不少,可是最经常使用的场景就是Ajax。好比:
function getData(method, url){ var promise = new Promise(function(resolve, reject){ //Ajax获取数据的代码... if(success){ resolve(response) }else{ reject(statusText) } }) return promise; } getData('get','www.xxx.com').then(Fun1).then(Fun2).then(Fun3).catch(function(reason){ //错误处理逻辑... });
更多关于es6的内容,能够关注右侧个人专栏--学习ES6。
参考:
MDN Javascript Promise.
Promise介绍-基础篇.《深刻理解ES6》-- Promise与异步编程。