JavaScript Promise 對(duì)象

Promise 對(duì)象有以下兩個(gè)特點(diǎn):

1芍殖、對(duì)象的狀態(tài)不受外界影響熄驼。Promise 對(duì)象代表一個(gè)異步操作郭蕉,有三種狀態(tài):
-pending: 初始狀態(tài)蛤售,不是成功或失敗狀態(tài)丁鹉。

  • fulfilled: 意味著操作成功完成。
    -rejected: 意味著操作失敗悴能。
    只有異步操作的結(jié)果揣钦,可以決定當(dāng)前是哪一種狀態(tài),任何其他操作都無(wú)法改變這個(gè)狀態(tài)漠酿。這也是 Promise 這個(gè)名字的由來(lái)拂盯,它的英語(yǔ)意思就是「承諾」,表示其他手段無(wú)法改變记靡。

2、一旦狀態(tài)改變团驱,就不會(huì)再變摸吠,任何時(shí)候都可以得到這個(gè)結(jié)果。Promise 對(duì)象的狀態(tài)改變嚎花,只有兩種可能:從Pending變?yōu)?Resolved和從Pending 變?yōu)?code>Rejected寸痢。只要這兩種情況發(fā)生,狀態(tài)就凝固了紊选,不會(huì)再變了啼止,會(huì)一直保持這個(gè)結(jié)果。就算改變已經(jīng)發(fā)生了兵罢,你再對(duì)Promise對(duì)象添加回調(diào)函數(shù)献烦,也會(huì)立即得到這個(gè)結(jié)果。這與事件(Event)完全不同卖词,事件的特點(diǎn)是巩那,如果你錯(cuò)過(guò)了它,再去監(jiān)聽(tīng)此蜈,是得不到結(jié)果的即横。

Promise 優(yōu)缺點(diǎn)

有了 Promise 對(duì)象,就可以將異步操作以同步操作的流程表達(dá)出來(lái)裆赵,避免了層層嵌套的回調(diào)函數(shù)东囚。此外,Promise 對(duì)象提供統(tǒng)一的接口战授,使得控制異步操作更加容易页藻。

Promise 也有一些缺點(diǎn)桨嫁。首先,無(wú)法取消 Promise惕橙,一旦新建它就會(huì)立即執(zhí)行瞧甩,無(wú)法中途取消。其次弥鹦,如果不設(shè)置回調(diào)函數(shù)肚逸,Promise 內(nèi)部拋出的錯(cuò)誤,不會(huì)反應(yīng)到外部彬坏。第三朦促,當(dāng)處于 Pending 狀態(tài)時(shí),無(wú)法得知目前進(jìn)展到哪一個(gè)階段(剛剛開(kāi)始還是即將完成)栓始。

Promise 創(chuàng)建

要想創(chuàng)建一個(gè) promise 對(duì)象务冕、可以使用 new 來(lái)調(diào)用 Promise 的構(gòu)造器來(lái)進(jìn)行實(shí)例化。

下面是創(chuàng)建 promise 的步驟:

var promise = new Promise(function(resolve, reject) {
    // 異步處理
    // 處理結(jié)束后幻赚、調(diào)用resolve 或 reject
});

Promise 構(gòu)造函數(shù)包含一個(gè)參數(shù)和一個(gè)帶有 resolve(解析)reject(拒絕)兩個(gè)參數(shù)的回調(diào)禀忆。在回調(diào)中執(zhí)行一些操作(例如異步),如果一切都正常落恼,則調(diào)用resolve箩退,否則調(diào)用reject

var myFirstPromise = new Promise(function(resolve, reject){
    //當(dāng)異步代碼執(zhí)行成功時(shí)佳谦,我們才會(huì)調(diào)用resolve(...), 當(dāng)異步代碼失敗時(shí)就會(huì)調(diào)用reject(...)
    //在本例中戴涝,我們使用setTimeout(...)來(lái)模擬異步代碼,實(shí)際編碼時(shí)可能是XHR請(qǐng)求或是HTML5的一些API方法.
    setTimeout(function(){
        resolve("成功!"); //代碼正常執(zhí)行钻蔑!
    }, 250);
});
 
myFirstPromise.then(function(successMessage){
    //successMessage的值是上面調(diào)用resolve(...)方法傳入的值.
    //successMessage參數(shù)不一定非要是字符串類型啥刻,這里只是舉個(gè)例子
    document.write("Yay! " + successMessage);
});

對(duì)于已經(jīng)實(shí)例化過(guò)的promise 對(duì)象可以調(diào)用promise.then()方法,傳遞resolve 和 reject 方法作為回調(diào)咪笑。

promise.then() 是 promise 最為常用的方法可帽。

promise.then(onFulfilled, onRejected)

promise簡(jiǎn)化了對(duì)error的處理,上面的代碼我們也可以這樣寫(xiě):

promise.then(onFulfilled).catch(onRejected)

Promise Ajax

下面是一個(gè)用 Promise 對(duì)象實(shí)現(xiàn)的 Ajax 操作的例子窗怒。

function ajax(URL) {
    return new Promise(function (resolve, reject) {
        var req = new XMLHttpRequest(); 
        req.open('GET', URL, true);
        req.onload = function () {
        if (req.status === 200) { 
                resolve(req.responseText);
            } else {
                reject(new Error(req.statusText));
            } 
        };
        req.onerror = function () {
            reject(new Error(req.statusText));
        };
        req.send(); 
    });
}
var URL = "/try/ajax/testpromise.php"; 
ajax(URL).then(function onFulfilled(value){
    document.write('內(nèi)容是:' + value); 
}).catch(function onRejected(error){
    document.write('錯(cuò)誤:' + error); 
});

上面代碼中蘑拯,resolve方法和 reject 方法調(diào)用時(shí),都帶有參數(shù)兜粘。它們的參數(shù)會(huì)被傳遞給回調(diào)函數(shù)申窘。reject 方法的參數(shù)通常是 Error 對(duì)象的實(shí)例,而resolve方法的參數(shù)除了正常的值以外孔轴,還可能是另一個(gè)Promise實(shí)例剃法,比如像下面這樣。

var p1 = new Promise(function(resolve, reject){
  // ... some code
});
 
var p2 = new Promise(function(resolve, reject){
  // ... some code
  resolve(p1);
})

上面代碼中路鹰,p1 和 p2都是Promise 的實(shí)例贷洲,但是p2resolve方法將p1作為參數(shù)收厨,這時(shí) p1的狀態(tài)就會(huì)傳遞給p2。如果調(diào)用的時(shí)候优构,p1 的狀態(tài)是 pending诵叁,那么 p2的回調(diào)函數(shù)就會(huì)等待p1的狀態(tài)改變;如果 p1的狀態(tài)已經(jīng)是fulfilled 或者 rejected钦椭,那么p2 的回調(diào)函數(shù)將會(huì)立刻執(zhí)行拧额。

Promise.prototype.then方法:鏈?zhǔn)讲僮?/h3>

Promise.prototype.then 方法返回的是一個(gè)新的 Promise 對(duì)象,因此可以采用鏈?zhǔn)綄?xiě)法

getJSON("/posts.json").then(function(json) {
  return json.post;
}).then(function(post) {
  // proceed
});

上面的代碼使用 then 方法彪腔,依次指定了兩個(gè)回調(diào)函數(shù)侥锦。第一個(gè)回調(diào)函數(shù)完成以后,會(huì)將返回結(jié)果作為參數(shù)德挣,傳入第二個(gè)回調(diào)函數(shù)恭垦。

如果前一個(gè)回調(diào)函數(shù)返回的是Promise對(duì)象,這時(shí)后一個(gè)回調(diào)函數(shù)就會(huì)等待該P(yáng)romise對(duì)象有了運(yùn)行結(jié)果格嗅,才會(huì)進(jìn)一步調(diào)用番挺。

getJSON("/post/1.json").then(function(post) {
  return getJSON(post.commentURL);
}).then(function(comments) {
  // 對(duì)comments進(jìn)行處理
});

這種設(shè)計(jì)使得嵌套的異步操作,可以被很容易得改寫(xiě)屯掖,從回調(diào)函數(shù)的"橫向發(fā)展"改為"向下發(fā)展"建芙。

Promise.prototype.catch方法:捕捉錯(cuò)誤

Promise.prototype.catch方法是Promise.prototype.then(null, rejection)的別名,用于指定發(fā)生錯(cuò)誤時(shí)的回調(diào)函數(shù)

getJSON("/posts.json").then(function(posts) {
  // some code
}).catch(function(error) {
  // 處理前一個(gè)回調(diào)函數(shù)運(yùn)行時(shí)發(fā)生的錯(cuò)誤
  console.log('發(fā)生錯(cuò)誤懂扼!', error);
});

Promise 對(duì)象的錯(cuò)誤具有"冒泡"性質(zhì),會(huì)一直向后傳遞右蒲,直到被捕獲為止阀湿。也就是說(shuō),錯(cuò)誤總是會(huì)被下一個(gè) catch 語(yǔ)句捕獲瑰妄。

getJSON("/post/1.json").then(function(post) {
  return getJSON(post.commentURL);
}).then(function(comments) {
  // some code
}).catch(function(error) {
  // 處理前兩個(gè)回調(diào)函數(shù)的錯(cuò)誤
});

Promise.all方法陷嘴,Promise.race方法

Promise.all 方法用于將多個(gè) Promise 實(shí)例,包裝成一個(gè)新的 Promise 實(shí)例间坐。

var p = Promise.all([p1,p2,p3]);

上面代碼中灾挨,Promise.all 方法接受一個(gè)數(shù)組作為參數(shù),p1竹宋、p2劳澄、p3 都是 Promise 對(duì)象的實(shí)例。(Promise.all 方法的參數(shù)不一定是數(shù)組蜈七,但是必須具有 iterator 接口秒拔,且返回的每個(gè)成員都是 Promise 實(shí)例。)

p 的狀態(tài)由 p1飒硅、p2砂缩、p3 決定作谚,分成兩種情況。

  • (1)只有p1庵芭、p2妹懒、p3的狀態(tài)都變成fulfilled,p的狀態(tài)才會(huì)變成fulfilled双吆,此時(shí)p1眨唬、p2、p3的返回值組成一個(gè)數(shù)組伊诵,傳遞給p的回調(diào)函數(shù)单绑。
  • (2)只要p1、p2曹宴、p3之中有一個(gè)被rejected搂橙,p的狀態(tài)就變成rejected,此時(shí)第一個(gè)被reject的實(shí)例的返回值笛坦,會(huì)傳遞給p的回調(diào)函數(shù)区转。
    下面是一個(gè)具體的例子。
// 生成一個(gè)Promise對(duì)象的數(shù)組
var promises = [2, 3, 5, 7, 11, 13].map(function(id){
  return getJSON("/post/" + id + ".json");
});
 
Promise.all(promises).then(function(posts) {
  // ...  
}).catch(function(reason){
  // ...
});

Promise.race 方法同樣是將多個(gè) Promise 實(shí)例版扩,包裝成一個(gè)新的 Promise 實(shí)例废离。

var p = Promise.race([p1,p2,p3]);

上面代碼中,只要p1礁芦、p2蜻韭、p3之中有一個(gè)實(shí)例率先改變狀態(tài),p的狀態(tài)就跟著改變柿扣。那個(gè)率先改變的Promise實(shí)例的返回值肖方,就傳遞給p的返回值。

如果`Promise.all方法和Promise.race方法的參數(shù)未状,不是Promise實(shí)例俯画,就會(huì)先調(diào)用下面講到的Promise.resolve方法,將參數(shù)轉(zhuǎn)為Promise實(shí)例司草,再進(jìn)一步處理艰垂。

Promise.resolve 方法,Promise.reject 方法

有時(shí)需要將現(xiàn)有對(duì)象轉(zhuǎn)為Promise對(duì)象埋虹,Promise.resolve方法就起到這個(gè)作用猜憎。

var jsPromise = Promise.resolve($.ajax('/whatever.json'));

上面代碼將 jQuery 生成 deferred 對(duì)象,轉(zhuǎn)為一個(gè)新的 ES6 的 Promise 對(duì)象搔课。

如果 Promise.resolve 方法的參數(shù)拉宗,不是具有then 方法的對(duì)象(又稱 thenable 對(duì)象),則返回一個(gè)新的 Promise 對(duì)象,且它的狀態(tài)為fulfilled旦事。

var p = Promise.resolve('Hello');
 
p.then(function (s){
  console.log(s)
});
// Hello

上面代碼生成一個(gè)新的Promise對(duì)象的實(shí)例p魁巩,它的狀態(tài)為fulfilled,所以回調(diào)函數(shù)會(huì)立即執(zhí)行姐浮,Promise.resolve方法的參數(shù)就是回調(diào)函數(shù)的參數(shù)谷遂。

如果Promise.resolve方法的參數(shù)是一個(gè)Promise對(duì)象的實(shí)例,則會(huì)被原封不動(dòng)地返回卖鲤。

Promise.reject(reason)方法也會(huì)返回一個(gè)新的Promise實(shí)例肾扰,該實(shí)例的狀態(tài)為rejected。Promise.reject方法的參數(shù)reason蛋逾,會(huì)被傳遞給實(shí)例的回調(diào)函數(shù)集晚。

var p = Promise.reject('出錯(cuò)了');
 
p.then(null, function (s){
  console.log(s)
});
// 出錯(cuò)了
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市区匣,隨后出現(xiàn)的幾起案子偷拔,更是在濱河造成了極大的恐慌,老刑警劉巖亏钩,帶你破解...
    沈念sama閱讀 219,188評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件莲绰,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡姑丑,警方通過(guò)查閱死者的電腦和手機(jī)蛤签,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,464評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)栅哀,“玉大人震肮,你說(shuō)我怎么就攤上這事×羰埃” “怎么了戳晌?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,562評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)间驮。 經(jīng)常有香客問(wèn)我,道長(zhǎng)马昨,這世上最難降的妖魔是什么竞帽? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,893評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮鸿捧,結(jié)果婚禮上屹篓,老公的妹妹穿的比我還像新娘。我一直安慰自己匙奴,他們只是感情好堆巧,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,917評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般谍肤。 火紅的嫁衣襯著肌膚如雪啦租。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,708評(píng)論 1 305
  • 那天荒揣,我揣著相機(jī)與錄音篷角,去河邊找鬼。 笑死系任,一個(gè)胖子當(dāng)著我的面吹牛恳蹲,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播俩滥,決...
    沈念sama閱讀 40,430評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼嘉蕾,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了霜旧?” 一聲冷哼從身側(cè)響起错忱,我...
    開(kāi)封第一講書(shū)人閱讀 39,342評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎颁糟,沒(méi)想到半個(gè)月后航背,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,801評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡棱貌,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,976評(píng)論 3 337
  • 正文 我和宋清朗相戀三年玖媚,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片婚脱。...
    茶點(diǎn)故事閱讀 40,115評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡今魔,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出障贸,到底是詐尸還是另有隱情错森,我是刑警寧澤,帶...
    沈念sama閱讀 35,804評(píng)論 5 346
  • 正文 年R本政府宣布篮洁,位于F島的核電站裳扯,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏摊阀。R本人自食惡果不足惜披泪,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,458評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望篷牌。 院中可真熱鬧睡蟋,春花似錦、人聲如沸枷颊。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,008評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至信卡,卻和暖如春隔缀,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背坐求。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,135評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工蚕泽, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人桥嗤。 一個(gè)月前我還...
    沈念sama閱讀 48,365評(píng)論 3 373
  • 正文 我出身青樓须妻,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親泛领。 傳聞我的和親對(duì)象是個(gè)殘疾皇子荒吏,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,055評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容