Promise對象的含義
所謂的
Promise
就是一個對象,用來傳遞異步操作的消息提澎,它代表某個未來才會知道結(jié)果的事件(異步操作)扫尖。
Promise對象特點
1. 該對象的狀態(tài)不受外界影響卸夕。
Promise
代表一個異步操作逝慧,它有三種狀態(tài):
Pending
(進(jìn)行中
)、Resolved
(已完成
)捌浩、Rejected
(已失敗
)
只有異步操作的結(jié)果可以決定當(dāng)前是那一種狀態(tài)放刨,任何其他操作都不能改變這個狀態(tài)。這也是 “Promise” 這個名字的由來尸饺。
2. 一旦狀態(tài)改變就不會再變进统,任何操作都能得到這個結(jié)果助币。
Promise
對象的狀態(tài)改變只有兩種可能
從Pending
變?yōu)?strong>Resolved
或者從 Pending
變?yōu)?strong>Rejected
。只要其中一個發(fā)生麻昼,狀態(tài)就凝固不會再變奠支,一直保持這個結(jié)果。
Promise對象缺點
- 首先抚芦,無法取消
Promise
倍谜,一旦新建它就會立即執(zhí)行,無法中途取消叉抡。- 其次尔崔,如果不設(shè)置回調(diào)函數(shù),
Promise
內(nèi)部拋出的錯誤不會反應(yīng)到外部褥民。- 再者季春,當(dāng)處于
Pending
狀態(tài)時,無法得知目前進(jìn)展到哪一個階段(剛剛開始還是即將完成
)消返。
基本用法
Promise
對象是一個構(gòu)造函數(shù)载弄,用來生成Promise
實例。
var promise = new Promise(function(resolve,reject){
//邏輯代碼
if(/*異步成功*/){
resolve(value);
} else{
reject(error)
}
});
Promise
構(gòu)造函數(shù)接受一個函數(shù)作為參數(shù)撵颊,改函數(shù)的兩個參數(shù)分別是resolve
和reject
宇攻,它們是兩個函數(shù),由javaScript
引擎提供倡勇,不用自己部署逞刷。
resolve
函數(shù)的作用,將Promise
函數(shù)的狀態(tài)從 未完成 變成 成功(從Pending
變?yōu)?strong>Resolved
),在異步操作成功時調(diào)用妻熊,并將異步操作的結(jié)果作為參數(shù)傳遞出去夸浅;reject
函數(shù)的作用,將Promise
函數(shù)的狀態(tài)從 未完成 變成 失敗(從Pending
變?yōu)?strong>Rejected
),在異步操作失敗時調(diào)用扔役,并將異步操作報出的錯誤作為參數(shù)傳遞出去帆喇;
Promise
實例生成以后,可以用then
方法分別指定Resolved
狀態(tài)和Rejected
狀態(tài)的回調(diào)函數(shù)
promise.then(function(value){
//success
},function(value){
//failure
})
then
方法可以接受兩個回調(diào)函數(shù)作為參數(shù)厅目。第一個函數(shù)是Promise
對象的狀態(tài)變?yōu)?strong>Resolved
時調(diào)用番枚,第二個是變?yōu)?strong>Rejected
時調(diào)用。第二個參數(shù)是可選的损敷,不一定要提供。這兩個函數(shù)都接受Promise
對象傳出的值作為參數(shù)深啤。
function timeout(ms){
return new Promise((resolv,reject)=>{
setTimeout(resolve,ms,'done')
});
}
timeout(100).then(function(value){
console.log(value)
})
↑上面代碼timeout返回一個Promise
實例拗馒,表示一段時間以后才會發(fā)生的結(jié)果。過了指定的時間(ms)以后溯街,Promise
實例的狀態(tài)變?yōu)?strong>Resolved
诱桂,就會觸發(fā)then方法綁定的回調(diào)函數(shù)洋丐。
function loadImageAsync(url){
return new Promise(function(resolve,reject){
var image = new Image();
image.onload = function(){
resolve(image);
};
image.onerror = function(){
reject(new Error("無法加載圖片,地址:" + url ));
};
image.src = url;
})
}
↑上面是一個異步加載圖片的例子.
var getJSON = function(url){
var promise = new Promise(function(resolve,reject){
var client = new XMLHttpRequest();
client.open('GET',url);
client.onreadystatechange = handler;
client.responseType = 'json';
client.setRequestHeader('Accept','application/json');
client.send();
function handler(){
if(this.readyState !==4) {
return;
}
if(this.readyState !==200) {
resolve(this.response)
}else{
reject(new Error(this.statusText));
}
};
});
return promise;
}挥等;
getJSON("/posts.json").then(function(json){
console.log('contents:'+ json)
},function(error){
console.error('出錯了',error);
});
上面的代碼中友绝,getJSON
是對XMLHttpRequest
對象的封裝,用于發(fā)出一個針對JSON
數(shù)據(jù)的HTTP
請求肝劲,并返回一個Promise
對象迁客。需要注意的是,在getJson
內(nèi)部辞槐,resolve
函數(shù)和reject
函數(shù)調(diào)用時帶有參數(shù)掷漱。
Promise.prototype.then();
Promise
的實例具有then
方法。也就是說榄檬,then
方法是定義在原型對象Promise.prototype
上的卜范。
它的作用是為Promise
的實例添加狀態(tài)改變時的回調(diào)函數(shù)。
then
方法返回的是一個新的Promise
實例(注意鹿榜,不是原來那個Promise
的實例)
var p1 = new Promise(function(resolve,reject){
});
var p2 = p1.then(function(){})
p1
//Promise {<pending>}
p2
//Promise {<pending>}
p1 == p2
//false
↑上面可以看到p2是then
方法返回的新的Promise
實例海雪,因此可以采用鏈?zhǔn)綄懛ǎ?strong>then
方法后面再調(diào)用then
方法舱殿。
getJSON("/posts.json").then(function(json){
return json.post;
}).then(function(post){
//....
});
↑上面的代碼使用then
方法一次指定了兩個回調(diào)函數(shù)奥裸。第一個回調(diào)函數(shù)完成以后,會將返回結(jié)果作為參數(shù)傳入第二個回調(diào)函數(shù)怀薛。
Promise.prototype.catch();
Promise.prototype.catch()
是 Promise.prototype.then(null,reject)
的別名刺彩,用于指定發(fā)生錯誤時的回調(diào)函數(shù)。
getJSON("/posts.json").then(function(json){
return json.post;
}).then(post=> {
//....
}).cath(err=>{
/....
})
如果異步過程中拋出錯誤枝恋,機(jī)會被catch
方法指定的回調(diào)函數(shù)所捕獲创倔。(條件是Promise狀態(tài)還未變成resolved
,否則拋出的錯是無效的。)
var p1 = new Promise(function(resilve,reject){
throw new Error('test')
});
p1.then(success=>{}).catch(err=>{console.log(err)})
// Error: test
var p2 = new Promise(function(resilve,reject){
resilve('success');
throw new Error('test')
});
p2.then(suc=>{console.log(suc)}).catch(err=>{console.log(err)})
//success
Promise
對象的錯誤具有“冒泡”性質(zhì)焚碌,會一直向后傳遞畦攘,直到被捕獲位置,也就是說錯誤總是會被下一個catch語句捕獲十电。
getJSON("/posts.json").then(function(json){
return json.post;
}).then(post=> {
//....
}).cath(err=>{
//處理前面三個Promise產(chǎn)生的錯誤
})
正規(guī)的寫法不要在then
方法中寫rejected
的狀態(tài)回調(diào)函數(shù)(then的第二個參數(shù))知押,而是使用catch
方法。
catch
方法返回的也是一個Promise
實例鹃骂,所以在Promise
方法在未拋出錯誤的時候台盯,catch
方法后面還可以繼續(xù)調(diào)用then
方法。
Promise.all();
Promise.all
方法用于將多個Promise
實例包裝成一個新的Promise
實例畏线。
var p = Promise.all([p1, p2, p3]);
Promise.all
接受一個數(shù)組作為參數(shù)静盅,如果非數(shù)組,就會先調(diào)用Promise.resolve
方法寝殴,將參數(shù)轉(zhuǎn)為Promise
實例蒿叠,再進(jìn)一步處理明垢。
Promise.all
方法的參數(shù)不一定是數(shù)組,但必須具有Iterator接口市咽,且返回的每個成員都是Promise.
實例痊银。
上面的代碼p
的狀態(tài)由p1, p2, p3
決定,分兩種情況施绎。
- 只有
p1, p2, p3
的狀態(tài)都變成Fulfilled
溯革,p
的狀態(tài)才會變成Fulfilled
,此時p1, p2, p3
的返回值組成一個數(shù)組,傳遞給你p
的回調(diào)函數(shù)粘姜。- 只要
p1, p2, p3
中的狀態(tài)有一個變成Rejected
鬓照,此時第一個被Rejected
實例返回值會傳遞給p
的回調(diào)函數(shù)。
Promise.race();
Promise.all();
同樣是將多個Promise
實例包裝成一個新的Promise
實例孤紧。
var p = Promise.race([p1, p2, p3]);
Promise.race
的參數(shù)規(guī)則和上面講到的Promise.all
方法一樣豺裆。
p
的狀態(tài)規(guī)則不一樣,只要p1, p2, p3
的狀態(tài)中有一個實例率先改變,p
的狀態(tài)也跟著改變号显。
Promise.resolve();
有時我們需要把現(xiàn)有對象轉(zhuǎn)為Promise
對象臭猜,Promise.resolve
方法就可以做到。
var jsPromise = Promise.resolve($.ajax('/whatever.json'));
上面的代碼將jQuery
生成的deferred
對象轉(zhuǎn)為新的Promise
對象押蚤。
Promise.resolve('foo');
↑上面等價于下面的寫法蔑歌;
new Promise(resolve=>resolve('foo'))
Promise.reject();
Promise.reject(reason)
也返回一個新的Promise
實例,狀態(tài)為rejected
,它的的參數(shù)reason
會被傳遞給實例的回調(diào)函數(shù)揽碘。
var p = Promise('出錯了4瓮馈!')雳刺;
↑等同于↓
var p = new Promise((resolve,reject)=>reject('出錯了=僭睢!'));
p.then(null,(s)=>{console.log(s)})
//出錯了R磋搿本昏!