Promise的含义
Promise是异步编程的一种解决方案,比传统的解决方案–回调函数和事件–更合理和更强大。
所谓promise
,简单来说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise是一个对象,从它可以获取异步操作的信息。Promise提供统一的API,各种异步操作都可以用同样的方法进行处理。
Promise
对象有以下两个特点。
(1)对象的状态不受外部影响。Promise
对象代表一个异步操作,有三种状态:Pending
(进行中)、Fulfilled
(已成功)和Rejected
(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是Promise
这个名字由来,它的意思是“承诺”,表示其他手段无法改变。
(2)一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise
对象的状态改变,只有两种可能:从Pending
变为Fulfiled
和从Pending
变为Rejected
。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这是就称为Resolved(已定型)。如果改变已经发生了,你再对Promise
对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过它,再去监听,是得不到结果的。
Promise
也有一些缺点。首先无法取消Promise
,一旦新建它就会立即执行,无法中途取消。其次,如果不设置回调函数,Promise
内部抛出的错误,不会反应到外部。第三,当处于Pending
状态时,无法得知目前进展到哪个阶段(刚刚开始还是即将完成)。
resolve 和 reject
下面创建一个Promise
实例。
function helloWorld(flag){ return new Promise(function(resolve, reject){ if(flag){ resolve('成功') }else{ reject('失败') } }) }
Promise
构造函数接受一个函数作为参数,该函数的两个参数分别是resolve
和reject
。它们是两个函数,由Javascript引擎提供,不用自己部署。
resolve
函数作用:将Promise对象状态变为“成功”;一般在异步操作成功时调用,并将异步操作的结果,作为参数传递出去。reject
函数作用:将Promise对象状态变为“失败”;一般在异步操作失败时调用,并将异步操作的错误,作为参数传递出去。
then 和 catch
promise
实例生成以后,可以用then
方法分别指定Resolved
状态和Rejected
状态的回调函数。
then
方法可以接受两个回调函数作为参数。第一个回调函数是Promise
对象的状态变为Resolved
时调用,第二个回调函数是Promise
对象的状态变为Rejected
时调用。第二个参数是可选的。这两个函数都接受Promise
对象传出的值作为参数。
catch
方法是then(onFulfiled, onRejected)
方法当中onRejected
函数的一个简单的写法,也就是说可以写成then(fn).catch(fn)
,相当于then(fn).then(null, fn)
。使用catch
的写法比一般的写法更清晰明确。
helloWorld(true).then(function(){ //success `Resolved`状态 },function(){ //failure `Rejected`状态 }) //then..catch 方式 helloWorld(true).then(function(){ //success `Resolved`状态 }).catch(function(){ //failure `Rejected`状态 })
Promise.all 和 Promise.race
Promise.all
方法用于将多个Promise实例,包装成一个新的Promise实例。
Promise.all
可以接收一个元素为Promise对象的数组作为参数,当这个数组里面所有的Promise对象都变为resolve
时,该方法才会返回。
var pro2 = new Promise((res, rej) => { setTimeout(function () { console.log('222'); res('222'); }, 5000); }) var pro3 = new Promise((res, rej) => { setTimeout(function () { console.log('333'); res('333'); }, 1000); }) var pro4 = new Promise((res, rej) => { setTimeout(function () { console.log('444'); res('444'); }, 4000); }) Promise.all([pro2, pro3, pro4]).then(function(result){ console.log(result) }) // 333, 444, 222 // [222, 333, 444]
注意点:[pro2, pro3, pro4]
中的promise没有依赖关系,不会按顺序执行。但是返回的值都会按照顺序且返回一个数组。
Promise.race
同样接收一个数组,不同的是只要数组中Promise对象中有一个率先改变状态(无论是resolve还是reject),该方法就会返回。
Promise.resolve
有时需要将现有对象转为Promise对象,Promise.resolve
方法就起到这个作用。
var res = Promise.resolve('foo') // 等价于 var res = new Promise(resolve => resolve('foo'))
Promise.reslove
方法的参数分为四种情况:
参数时Promise实例
那么
Promise.resolve
将不做任何修改,原封不动地返回这个实例参数是一个
thenable
对象(指的是具有then
方法的对象)Promise.resolve
方法会将这个对象转为Promise对象,然后就立即执行thenable
对象的then
方法。let thenable = { then: function(resolve, reject) { resolve(42); } }; let p1 = Promise.resolve(thenable); p1.then(function(value) { console.log(value); // 42 });
参数不是具有
then
方法的对象,或根本就不是对象返回Promise对象,状态为
Resolved
不带有任何参数
直接返回一个
Resolved
状态的Promise对象。
Promise.reject
Promise.reject()
返回一个新的Promise
实例,且该实例状态为rejected
。
var p = Promise.reject('出错了'); // 等同于 var p = new Promise((resolve, reject) => reject('出错了'))
注意:参数会原封不动的传递给后续方法的参数。(不会执行thenable
中的then
方法)
done 和 finally
done
Promise对象的回调链,不管以then
方法或catch
方法结尾,要是最后一个方法抛出错误,都有可能无法捕捉到(因为Promise内部的错误不会冒泡到全局)。因此,我们可以提供一个done
方法,总是处于回调链的尾端,保证抛出任何可能出现的错误。
asyncFunc() .then(f1) .catch(r1) .then(f2) .done();
finally
finally
方法用于指定不管Promise对象最后状态如何,都会执行的操作。它与done
方法的最大区别,它接受一个普通的回调函数作为参数,该函数不管怎样都必须执行。
helloWorld(true) .then(function () { // run test }) .finally(function(){ // 必然执行 });
Demo实践
多个异步问题
function pro(flag) { return new Promise((res, rej) => { if (flag) { res('success') } else { rej('fail') } }) } function pro2() { return new Promise((res, rej) => { setTimeout(function () { console.log('222'); res(); }, 5000); }) } function pro3() { return new Promise((res, rej) => { setTimeout(function () { console.log('333'); res(); }, 1000); }) } function pro4() { return new Promise((res, rej) => { setTimeout(function () { console.log('444'); res(); }, 4000); }) } function pro5() { return new Promise((res, rej) => { setTimeout(function () { console.log('555'); res(); }, 1000); }) } pro(true) .then((res) => { console.log(res); }) .then(pro2) .then(pro3) .then(pro4) .then(pro5) //success 222 333 444 555
单纯的回调,前者return值可以作为后续操作的参数
function helloWorld(flag){ return new Promise(function(resolve, reject){ if(flag){ resolve('成功') }else{ reject('失败') } }) }; helloWorld(true).then(function (message) { return message; }).then(function (message) { return message + ' World'; }).then(function (message) { return message + '!'; }).then(function (message) { alert(message); }); // 成功 World! helloWorld(false).then(function (message) { return message; }).then(function (message) { return message + ' World'; }).then(function (message) { return message + '!'; }).then(function (message) { alert(message); }).catch(function(message){ alert(message) }) // 失败
总结
new Promise(fn(resolve, reject))
接收一个函数fn(resolve, reject)
两个参数都是函数.then(fn)
接收一个函数,且then
执行后返回一个Promise,所以能够继续then下去Promise
和then
接收的函数如同setTimeOut一样“自执行”