一、Promise 的含義
??Promise 是異步編程的一種解決方案,比傳統(tǒng)的解決方案 回調(diào)函數(shù)和事件更合理和更強大。它由社區(qū)最早提出和實現(xiàn),ES6將其寫進了語言標(biāo)準(zhǔn),統(tǒng)一了語法,原生提供了Promise。
??所謂Promise,簡單說就是一個容器,里面保存著某個未來才回結(jié)束的事件(通常是一個異步操作)的結(jié)果。從語法上說,Promise是一個對象,從它可以獲取異步操作的消息。
Promise對象有以下兩個特點
(1) 對象的狀態(tài)不受外界影響。
Promise對象代表一個異步操作,有三種狀態(tài):
- pending:進行中
- fulfilled :已經(jīng)成功
- rejected 已經(jīng)失敗
??(注意:只有異步操作的結(jié)果,可以決定當(dāng)前是哪一種狀態(tài),任何其他操作都無法改變這個狀態(tài)。)
(2) 對象的狀態(tài)改變,只有兩種可能:
- 從pending變?yōu)閒ulfilled
- 從pending變?yōu)閞ejected。
??(注意:只要這兩種情況發(fā)生,狀態(tài)就凝固了,不會再變了,會一直保持這個結(jié)果,這時就稱為resolved(已定型)。如果改變已經(jīng)發(fā)生了,你再對Promise對象添加回調(diào)函數(shù),也會立即得到這個結(jié)果。這與事件(Event)完全不同,事件的特點是,如果你錯過了它,再去監(jiān)聽,是得不到結(jié)果的。)
??(注意,為了行文方便,本章后面的resolved統(tǒng)一只指fulfilled狀態(tài),不包含rejected狀態(tài)。)
Promise對象的優(yōu)缺點
(1)Promise對象的優(yōu)點:
- 可以將異步操作以同步操作的流程表達出來,避免了層層嵌套的回調(diào)函數(shù);
- 2 .
Promise對象提供統(tǒng)一的接口,使得控制異步操作更加容易。
(2)Promise對象的缺點:
- 無法取消Promise,一旦新建它就會立即執(zhí)行,無法中途取消。;
- 如果不設(shè)置回調(diào)函數(shù),
Promise內(nèi)部拋出的錯誤,不會反應(yīng)到外部;
- 當(dāng)處于pending狀態(tài)時,無法得知目前進展到哪一個階段(剛剛開始還是即將完成)。
二、Promise 的基本用法
??ES6 規(guī)定,Promise對象是一個構(gòu)造函數(shù),用來生成Promise實例。
??下面代碼創(chuàng)造了一個Promise實例。
const promise = new Promise(function(resolve, reject) {
// ... some code
if (/* 異步操作成功 */){
resolve(value);
} else {
reject(error);
}
});
??Promise構(gòu)造函數(shù)接受一個函數(shù)作為參數(shù),該函數(shù)的兩個參數(shù)分別是resolve和reject。它們是兩個函數(shù),由 JavaScript 引擎提供,不用自己部署。
resolve函數(shù)的作用是,將Promise對象的狀態(tài)從“未完成”變?yōu)椤俺晒Α保磸?pending變?yōu)?resolved),在異步操作成功時調(diào)用,并將異步操作的結(jié)果,作為參數(shù)傳遞出去;reject函數(shù)的作用是,將Promise對象的狀態(tài)從“未完成”變?yōu)椤笆 保磸?pending變?yōu)?rejected),在異步操作失敗時調(diào)用,并將異步操作報出的錯誤,作為參數(shù)傳遞出去。
下面舉一個Promise對象的簡單例子。
function timeout(ms) {
return new Promise((resolve, reject) => {
setTimeout(resolve, ms, 'done');
});
}
timeout(100).then((value) => {
console.log(value);
});
??上面代碼中,timeout方法返回一個Promise實例,表示一段時間以后才會發(fā)生的結(jié)果。過了指定的時間(ms參數(shù))以后,Promise實例的狀態(tài)變?yōu)?code>resolved,就會觸發(fā)then方法綁定的回調(diào)函數(shù)。
Promise 新建后就會立即執(zhí)行。
let promise = new Promise(function(resolve, reject) {
console.log('Promise');
resolve();
});
promise.then(function() {
console.log('resolved.');
});
console.log('Hi!');
// Promise
// Hi!
// resolved
??上面代碼中,Promise新建后會立即執(zhí)行,所以首先輸出的是Promise。然后then方法指定的回調(diào)函數(shù),將在當(dāng)前腳本所有同步任務(wù)執(zhí)行完才會執(zhí)行,所以resolved最后輸出。
下面是異步加載圖片的例子
function loadImageAsync(url) {
return new Promise(function(resolve, reject) {
const image = new Image();
image.onload = function() {
resolve(image);
};
image.onerror = function() {
reject(new Error('Could not load image at ' + url));
};
image.src = url;
});
}
??上面代碼中,使用Promise包裝了一個圖片加載的異步操作。如果加載成功,就調(diào)用resolve方法,否則就調(diào)用reject方法。
下面是一個用Promise對象實現(xiàn)的 Ajax 操作的例子
const getJSON = function(url) {
const promise = new Promise(function(resolve, reject){
const handler = function() {
if (this.readyState !== 4) {
return;
}
if (this.status === 200) {
resolve(this.response);
} else {
reject(new Error(this.statusText));
}
};
const client = new XMLHttpRequest();
client.open("GET", url);
client.onreadystatechange = handler;
client.responseType = "json";
client.setRequestHeader("Accept", "application/json");
client.send();
});
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ù)。)