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í)例贷洲,但是p2
的 resolve
方法將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ò)了