Promise 的含義
Promise 是異步編程的一種解決方案,比傳統(tǒng)的解決方案——回調(diào)函數(shù)和事件——更合理和更強大。它由社區(qū)最早提出和實現(xiàn),ES6 將其寫進了語言標準,統(tǒng)一了用法,原生提供了Promise對象。
Promise對象有以下兩個特點。
(1)對象的狀態(tài)不受外界影響。Promise對象代表一個異步操作,有三種狀態(tài):pending(進行中)、fulfilled(已成功)和rejected(已失?。V挥挟惒讲僮鞯慕Y(jié)果,可以決定當(dāng)前是哪一種狀態(tài),任何其他操作都無法改變這個狀態(tài)。
(2)一旦狀態(tài)改變,就不會再變,任何時候都可以得到這個結(jié)果。Promise對象的狀態(tài)改變,只有兩種可能:從pending變?yōu)?code>fulfilled和從pending變?yōu)?code>rejected。只要這兩種情況發(fā)生,狀態(tài)就凝固了,不會再變了,會一直保持這個結(jié)果,這時就稱為 resolved(已定型)。如果改變已經(jīng)發(fā)生了,你再對Promise對象添加回調(diào)函數(shù),也會立即得到這個結(jié)果。這與事件(Event)完全不同,事件的特點是,如果你錯過了它,再去監(jiān)聽,是得不到結(jié)果的。
基本用法
// resolve, reject 由 js 引擎部署
const p = new Promise((resolve, reject) => {
console.log('yo.')
// some code
// 是當(dāng)前的 Promise 對象的狀態(tài)從 'pending' 變?yōu)?'resolved' 成功
// 并且不再改變
resolve('hello resovle')
// reject 讓狀態(tài)從 'pending' 變?yōu)?'reject' 失敗
// reject(err)
})
// Promise 內(nèi)部的代碼,會在 new 的時候立即執(zhí)行
// Promise 狀態(tài)為 resolve 時,執(zhí)行 then()
p.then((value) => {
// resolve 會把他的參數(shù)傳給 then()
console.log(value) // 'hello resolve'
})
.catch((err) => {
// 在 Promise 狀態(tài)為 reject 時執(zhí)行
// 或 前面的 then() 發(fā)生錯誤時執(zhí)行
console.log(err)
})
方法精講
Promise 的常用 API 如下:
- Promise.resolve(value)
類方法,該方法返回一個以 value 值解析后的 Promise 對象 1、如果這個值是個 thenable(即帶有 then 方法),返回的 Promise 對象會“跟隨”這個 thenable 的對象,采用它的最終狀態(tài)(指 resolved/rejected/pending/settled)
2、如果傳入的 value 本身就是 Promise 對象,則該對象作為 Promise.resolve 方法的返回值返回。
3、其他情況以該值為成功狀態(tài)返回一個 Promise 對象。
上面是 resolve 方法的解釋,傳入不同類型的 value 值,返回結(jié)果也有區(qū)別。這個 API 比較重要,建議大家通過練習(xí)一些小例子,并且配合上面的解釋來熟悉它。如下幾個小例子:
//如果傳入的 value 本身就是 Promise 對象,則該對象作為 Promise.resolve 方法的返回值返回。
function fn(resolve){
setTimeout(function(){
resolve(123);
},3000);
}
let p0 = new Promise(fn);
let p1 = Promise.resolve(p0);
// 返回為true,返回的 Promise 即是 入?yún)⒌?Promise 對象。
console.log(p0 === p1);
復(fù)制代碼
傳入 thenable 對象,返回 Promise 對象跟隨 thenable 對象的最終狀態(tài)。
ES6 Promises 里提到了 Thenable 這個概念,簡單來說它就是一個非常類似 Promise 的東西。最簡單的例子就是 jQuery.ajax,它的返回值就是 thenable 對象。但是要謹記,并不是只要實現(xiàn)了 then 方法就一定能作為 Promise 對象來使用。
//如果傳入的 value 本身就是 thenable 對象,返回的 promise 對象會跟隨 thenable 對象的狀態(tài)。
let promise = Promise.resolve($.ajax('/test/test.json'));// => promise對象
promise.then(function(value){
console.log(value);
});
復(fù)制代碼
返回一個狀態(tài)已變成 resolved 的 Promise 對象。
let p1 = Promise.resolve(123);
//打印p1 可以看到p1是一個狀態(tài)置為resolved的Promise對象
console.log(p1)
復(fù)制代碼
- Promise.reject
類方法,且與 resolve 唯一的不同是,返回的 promise 對象的狀態(tài)為 rejected。
- Promise.prototype.then
實例方法,為 Promise 注冊回調(diào)函數(shù),函數(shù)形式:fn(vlaue){},value 是上一個任務(wù)的返回結(jié)果,then 中的函數(shù)一定要 return 一個結(jié)果或者一個新的 Promise 對象,才可以讓之后的then 回調(diào)接收。
- Promise.prototype.catch
實例方法,捕獲異常,函數(shù)形式:fn(err){}, err 是 catch 注冊 之前的回調(diào)拋出的異常信息。
- Promise.race
類方法,多個 Promise 任務(wù)同時執(zhí)行,返回最先執(zhí)行結(jié)束的 Promise 任務(wù)的結(jié)果,不管這個 Promise 結(jié)果是成功還是失敗。 。
- Promise.all
類方法,多個 Promise 任務(wù)同時執(zhí)行。
如果全部成功執(zhí)行,則以數(shù)組的方式返回所有 Promise 任務(wù)的執(zhí)行結(jié)果。 如果有一個 Promise 任務(wù) rejected,則只返回 rejected 任務(wù)的結(jié)果。
方法詳解
Promise.prototype.then()
Promise 實例具有then方法,也就是說,then方法是定義在原型對象Promise.prototype上的。它的作用是為 Promise 實例添加狀態(tài)改變時的回調(diào)函數(shù)。前面說過,then方法的第一個參數(shù)是resolved狀態(tài)的回調(diào)函數(shù),第二個參數(shù)(可選)是rejected狀態(tài)的回調(diào)函數(shù)。then方法返回的是一個新的Promise實例(注意,不是原來那個Promise實例)。因此可以采用鏈式寫法,即then方法后面再調(diào)用另一個then方法。
p.then(val => {
return val
// return val 會讓 val 作為 下一個 then() 的參數(shù)
}).then(val => {
console.log(val)
}).then(...)
Promise.prototype.catch()
Promise.prototype.catch方法是.then(null, rejection)或.then(undefined, rejection)的別名,用于指定發(fā)生錯誤時的回調(diào)函數(shù)。
p(val).then(() => {
// ...
}).catch((err) => {
// 處理 p(val) 和 前一個回調(diào)函數(shù)運行時發(fā)生的錯誤
console.log('發(fā)生錯誤!', err)
})
Promise.prototype.finally()
finally方法用于指定不管 Promise 對象最后狀態(tài)如何,都會執(zhí)行的操作。該方法是 ES2018 引入標準的。
promise
.then(result => {···})
.catch(error => {···})
.finally(() => {···});
上面代碼中,不管promise最后的狀態(tài),在執(zhí)行完then或catch指定的回調(diào)函數(shù)以后,都會執(zhí)行finally方法指定的回調(diào)函數(shù)。
Promise.all()
Promise.all方法用于將多個 Promise 實例,包裝成一個新的 Promise 實例。
const p = Promise.all([p1, p2, p3]);
上面代碼中,Promise.all方法接受一個數(shù)組作為參數(shù),p1、p2、p3都是 Promise 實例,如果不是,就會先調(diào)用下面講到的Promise.resolve方法,將參數(shù)轉(zhuǎn)為 Promise 實例,再進一步處理。(Promise.all方法的參數(shù)可以不是數(shù)組,但必須具有 Iterator 接口,且返回的每個成員都是 Promise 實例。)
p的狀態(tài)由p1、p2、p3決定,分成兩種情況。
(1)只有p1、p2、p3的狀態(tài)都變成fulfilled,p的狀態(tài)才會變成fulfilled,此時p1、p2、p3的返回值組成一個數(shù)組,傳遞給p的回調(diào)函數(shù)。
(2)只要p1、p2、p3之中有一個被rejected,p的狀態(tài)就變成rejected,此時第一個被reject的實例的返回值,會傳遞給p的回調(diào)函數(shù)。
Promise.race()
類似 Promise.all(), 區(qū)別在于 下面代碼中,只要p1、p2、p3之中有一個實例率先改變狀態(tài),p的狀態(tài)就跟著改變。那個率先改變的 Promise 實例的返回值,就傳遞給p的回調(diào)函數(shù)。
const p = Promise.race([p1, p2, p3]);