Promise的基礎知識
- Promise是什麼?
Promise是爲了解決異步編程中事件模型和回調模式的不足而產生的一種解決方案,在語法上來說,Promise是一個對象(object)。下圖中的promiseABC是我創建的一個promise。
- Promise的生命週期
每個Promise都有它們相應的生命週期,Promise的生命週期剛開始處於進行中(pending),此時Promise的 執行器函數 (此概念在下文會詳細講解)中的操作尚未完成;當操作結束後,Promise會進入到這兩個狀態中的其中一個:
①Fulfilled,代表Promise異步操作成功完成。②Rejected,代表Promise異步操作失敗。
內部屬性[[PromiseState]]表示Promise的三種狀態,也就是上文說的pending、fulfilled和rejected,但這個屬性不暴露在Promise對象上,不能通過console之類的編程方式來查看Promise的狀態,只有當Promise的狀態改變時,通過 then() 或 catch() 來進行相應的操作。
創建Promise的基本語法
- 創建新的Promise(創建未完成的Promise)
用Promise的構造函數來創建新的Promise,構造函數只接收一個參數:執行器(executor)函數。執行器接收兩個參數:resolve()函數和reject()函數。執行器成功完成時調用resolve(),反之調用reject()。
resolve()函數和reject()函數作爲形參當然 可以被隨意命名,我們只需要知道它們分別指向then中的第一個參數(完成處理函數)和第二個參數(拒絕處理函數)就行了。
舉個栗子:
let promiseABC = new Promise(function(resolve,reject){//這個function就是執行器
console.log("你好");//成功輸出"你好"
let a = 1
if(a==1){
resolve();//這裏會觸發resolve()函數
}
else{
reject();
}
})
在瀏覽器的控制檯我們輸入console.log(promiseABC);可以看到promiseABC是一個promise對象,它此時的狀態是resolved,代表執行器中的操作被成功執行,然是由於我們沒有定義resolve()函數,因此返回了一個undefined。
- 創建已處理的Promise
在上文中我們說到,當執行器內的操作結束後,會進入fulfilled狀態或rejected狀態。因此已處理的Promise要麼調用resolve()函數要麼調用reject()函數。
使用Promise.resolve()創建一個完成態的的Promise:
let pr = Promise.resolve("炒米粉");
pr.then(function(food){
console.log(food);//輸出"炒米粉"
});
這個Promise被創建後就是已完成的,因此它不存在拒絕狀態,它的reject()函數永遠不會被調用
使用Promise.reject()創建一個已拒絕的Promise:
let pr = Promise.reject("炒米粉");
pr.catch(function(food){
console.log(food);//輸出"炒米粉"
});
這個Promise被創建後就是已拒絕的,因此它不存在已完成的狀態,它的resolve()函數永遠不會被調用。
Promise的then()方法和catch()方法
-
then()方法
所有的Promise對象都有then()方法,它接收兩個參數,第一個是當Promise的狀態變爲fulfilled時要調用的函數,第二個是當Promise的狀態變爲rejected時要調用的函數。
所有與異步操作相關的數據都會傳遞給第一個函數,而當異步操作失敗時,所有的錯誤信息和附加數據都會傳遞給第二個函數。
then()的兩個參數都是可選的,換句話來說就是它的兩個參數都是可缺省的;但如果當執行器的reject()函數被調用時,then()卻沒有第二個參數,瀏覽器將會拋出一個錯誤。像這樣:
因此如果沒有特殊情況,最好要給每一個Promise都添加拒絕處理程序,也就是then()的第二個參數。 -
catch()方法
這個方法相當於只給then傳入第二個參數,也就是隻傳入拒絕處理程序。下面的這兩個代碼片是等價的。
let pr = new Promise(function(resolve,reject){
DOSOMETHING……
});
pr.then(null,function(){
DOSOMETHING……
});
let pr = new Promise(function(resolve,reject){
DOSOMETHING……
});
pr.catch(function(){
DOSOMETHING……
});
執行器的一些特性
執行器函數中的代碼會被立刻執行,而調用 resolve()函數 和 reject()函數 會觸發一個異步操作,異步操作會被最後執行。
舉個栗子:
let promiseABC = new Promise(function(resolve,reject){
console.log("你好");
reject();
});
promiseABC.catch(function(){
console.log("拒絕函數被中的代碼被執行");
});
console.log("你真好");
運行結果:
如果執行器函數中的代碼發生錯誤,那麼會直接調用拒絕函數。
Promise鏈
Promise可以串聯起來以實現更加複雜的異步操作。
舉個栗子:
let promiseABC = new Promise(function(resolve,reject){
console.log("你好");
resolve();
});
promiseABC.then(function(){
console.log("炒米粉");
}).then(function(){
console.log("炒雞蛋");
});
運行結果:
上面的代碼等價於:
let promiseABC = new Promise(function(resolve,reject){
console.log("你好");
resolve();
});
let pr = promiseABC.then(function(){
console.log("炒米粉");
});
pr.then(function(){
console.log("炒雞蛋");
});
Promise鏈也可以用於捕獲錯誤,做法與上面then()方法的串聯一樣,在這就不做代碼展示了,需要提出來的是,Promise鏈的末尾最好要留一個拒絕處理程序(可以是有第二個參數的then()函數,也可以是catch()函數)來確保無論在哪個位置發生了錯誤都能被處理。
Promise鏈的一個重要特性是能給下游Promise傳遞數據。
舉個栗子:
let promiseABC = new Promise(function(resolve,reject){
resolve(6);
});
promiseABC.then(function(num){
console.log(num);
return num+1;
}).then(function(num){
console.log(num);
});
運行結果:
上面的代碼中用的是resolve()函數,當然用reject()函數也是能傳遞數據的。