- Promise 的含義
- 基本用法
- Promise.prototype.then()
- Promise.prototype.catch()
- Promise.prototype.finally()
- Promise.all()
- Promise.race()
- Promise.resolve()
- Promise.reject()
- 應用
- Promise.try()
1 Promise 的含義 § ?
Promise 是異步編程的一種解決方案关霸,比傳統(tǒng)的解決方案——回調(diào)函數(shù)和事件——更合理和更強大缸逃。它由社區(qū)最早提出和實現(xiàn),ES6 將其寫進了語言標準辅愿,統(tǒng)一了用法豆瘫,原生提供了Promise對象。
所謂Promise踪少,簡單說就是一個容器批什,里面保存著某個未來才會結束的事件(通常是一個異步操作)的結果哆窿。從語法上說链烈,Promise 是一個對象,從它可以獲取異步操作的消息挚躯。Promise 提供統(tǒng)一的 API强衡,各種異步操作都可以用同樣的方法進行處理。
Promise
對象有以下兩個特點码荔。
(1)對象的狀態(tài)不受外界影響漩勤。Promise對象代表一個異步操作,有三種狀態(tài):pending(進行中)缩搅、fulfilled(已成功)和rejected(已失斣桨堋)。只有異步操作的結果硼瓣,可以決定當前是哪一種狀態(tài)究飞,任何其他操作都無法改變這個狀態(tài)置谦。這也是Promise這個名字的由來,它的英語意思就是“承諾”亿傅,表示其他手段無法改變媒峡。
(2)一旦狀態(tài)改變,就不會再變葵擎,任何時候都可以得到這個結果谅阿。Promise對象的狀態(tài)改變,只有兩種可能:從pending變?yōu)閒ulfilled和從pending變?yōu)閞ejected酬滤。只要這兩種情況發(fā)生签餐,狀態(tài)就凝固了,不會再變了盯串,會一直保持這個結果氯檐,這時就稱為 resolved(已定型)。如果改變已經(jīng)發(fā)生了嘴脾,你再對Promise對象添加回調(diào)函數(shù),也會立即得到這個結果蔬墩。這與事件(Event)完全不同译打,事件的特點是,如果你錯過了它拇颅,再去監(jiān)聽奏司,是得不到結果的。
注意樟插,為了行文方便韵洋,本章后面的resolved統(tǒng)一只指fulfilled狀態(tài),不包含rejected狀態(tài)黄锤。
有了Promise對象搪缨,就可以將異步操作以同步操作的流程表達出來,避免了層層嵌套的回調(diào)函數(shù)鸵熟。此外副编,Promise對象提供統(tǒng)一的接口,使得控制異步操作更加容易流强。
Promise也有一些缺點痹届。首先,無法取消Promise打月,一旦新建它就會立即執(zhí)行队腐,無法中途取消。其次奏篙,如果不設置回調(diào)函數(shù)柴淘,Promise內(nèi)部拋出的錯誤,不會反應到外部。第三悠就,當處于pending狀態(tài)時千绪,無法得知目前進展到哪一個階段(剛剛開始還是即將完成)。
如果某些事件不斷地反復發(fā)生梗脾,一般來說荸型,使用 Stream 模式是比部署
Promise
更好的選擇。
2 基本用法 § ?
ES6 規(guī)定炸茧,Promise對象是一個構造函數(shù)瑞妇,用來生成Promise實例。
const promise = new Promise(function(resolve, reject) {
// ... some code
if (/* 異步操作成功 */){
resolve(value);
} else {
reject(error);
}
});
Promise構造函數(shù)接受一個函數(shù)作為參數(shù)梭冠,該函數(shù)的兩個參數(shù)分別是resolve和reject辕狰。它們是兩個函數(shù),由 JavaScript 引擎提供控漠,不用自己部署蔓倍。
Promise實例生成以后,可以用then方法分別指定resolved狀態(tài)和rejected狀態(tài)的回調(diào)函數(shù)盐捷。
promise.then(function(value) {
// success
}, function(error) {
// failure
});
3 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方法。
4 Promise.prototype.catch() § ?
Promise.prototype.catch方法是.then(null, rejection)的別名淋叶,用于指定發(fā)生錯誤時的回調(diào)函數(shù)憔披。
getJSON('/posts.json').then(function(posts) {
// ...
}).catch(function(error) {
// 處理 getJSON 和 前一個回調(diào)函數(shù)運行時發(fā)生的錯誤
console.log('發(fā)生錯誤!', error);
});
Promise 對象的錯誤具有“冒泡”性質(zhì)爸吮,會一直向后傳遞芬膝,直到被捕獲為止。也就是說形娇,錯誤總是會被下一個catch語句捕獲锰霜。
getJSON('/post/1.json').then(function(post) {
return getJSON(post.commentURL);
}).then(function(comments) {
// some code
}).catch(function(error) {
// 處理前面三個Promise產(chǎn)生的錯誤
});
上面代碼中,一共有三個 Promise 對象:一個由getJSON產(chǎn)生桐早,兩個由then產(chǎn)生癣缅。它們之中任何一個拋出的錯誤厨剪,都會被最后一個catch捕獲。
5 Promise.prototype.finally() § ?
finally方法用于指定不管 Promise 對象最后狀態(tài)如何友存,都會執(zhí)行的操作祷膳。該方法是 ES2018 引入標準的。
6 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ù)。
7 Promise.race() § ?
Promise.race方法同樣是將多個 Promise 實例瓶埋,包裝成一個新的 Promise 實例希柿。
const p = Promise.race([p1, p2, p3]);
上面代碼中诊沪,只要p1、p2曾撤、p3之中有一個實例率先改變狀態(tài)端姚,p的狀態(tài)就跟著改變。那個率先改變的 Promise 實例的返回值挤悉,就傳遞給p的回調(diào)函數(shù)渐裸。
8 Promise.resolve() § ?
有時需要將現(xiàn)有對象轉(zhuǎn)為 Promise 對象,Promise.resolve方法就起到這個作用尖啡。
const jsPromise = Promise.resolve($.ajax('/whatever.json'));
上面代碼將 jQuery 生成的deferred對象橄仆,轉(zhuǎn)為一個新的 Promise 對象。
Promise.resolve等價于下面的寫法衅斩。
Promise.resolve('foo')
// 等價于
new Promise(resolve => resolve('foo'))
Promise.resolve方法的參數(shù)分成四種情況盆顾。
(1)參數(shù)是一個 Promise 實例
如果參數(shù)是 Promise 實例,那么Promise.resolve將不做任何修改畏梆、原封不動地返回這個實例您宪。
(2)參數(shù)是一個thenable對象
thenable對象指的是具有then方法的對象,比如下面這個對象奠涌。
let thenable = {
then: function(resolve, reject) {
resolve(42);
}
};
Promise.resolve方法會將這個對象轉(zhuǎn)為 Promise 對象宪巨,然后就立即執(zhí)行thenable對象的then方法。
let thenable = {
then: function(resolve, reject) {
resolve(42);
}
};
let p1 = Promise.resolve(thenable);
p1.then(function(value) {
console.log(value); // 42
});
上面代碼中溜畅,thenable對象的then方法執(zhí)行后捏卓,對象p1的狀態(tài)就變?yōu)閞esolved,從而立即執(zhí)行最后那個then方法指定的回調(diào)函數(shù)慈格,輸出 42怠晴。
(3)參數(shù)不是具有then方法的對象,或根本就不是對象
如果參數(shù)是一個原始值浴捆,或者是一個不具有then方法的對象蒜田,則Promise.resolve方法返回一個新的 Promise 對象,狀態(tài)為resolved选泻。
const p = Promise.resolve('Hello');
p.then(function (s){
console.log(s)
});
// Hello
上面代碼生成一個新的 Promise 對象的實例p冲粤。由于字符串Hello不屬于異步操作(判斷方法是字符串對象不具有 then 方法),返回 Promise 實例的狀態(tài)從一生成就是resolved页眯,所以回調(diào)函數(shù)會立即執(zhí)行梯捕。Promise.resolve方法的參數(shù),會同時傳給回調(diào)函數(shù)窝撵。
(4)不帶有任何參數(shù)
Promise.resolve方法允許調(diào)用時不帶參數(shù)傀顾,直接返回一個resolved狀態(tài)的 Promise 對象。
所以忿族,如果希望得到一個 Promise 對象锣笨,比較方便的方法就是直接調(diào)用Promise.resolve方法蝌矛。
const p = Promise.resolve();
p.then(function () {
// ...
});
上面代碼的變量p就是一個 Promise 對象。
需要注意的是错英,立即resolve的 Promise 對象入撒,是在本輪“事件循環(huán)”(event loop)的結束時,而不是在下一輪“事件循環(huán)”的開始時椭岩。
9 Promise.reject() § ?
Promise.reject(reason)方法也會返回一個新的 Promise 實例茅逮,該實例的狀態(tài)為rejected。