JavaScript中 Promise 的理解
Promise 是什麼?
Promise 是 ES6 提供的一種非同步程式設計解決方案,解決了傳統非同步處理的回撥金字塔問題; Promise 物件是一個建構函式,用來生成 Promise 例項;
Promise 構成:console.dir(Promise);
可以看出,Promise 這個建構函式自己身上有 resolve(), reject(), all(), race() 這幾種常見的方法,原型上有 catch(), then(), finally() 常用的方法,所以 Promise 的例項可以使用 catch(), then() 等方法;
Promise 的用法
-
Promise
// 傳遞一個函式作為例項化引數 const promise = new Promise(function(resolve, reject) { if (/* 非同步操作成功 */) { // 改變狀態為 fulfilled resolve(value); } else { // 改變狀態為 rejected reject(error); } });
-
resolve 函式的作用是將 Promise 物件的狀態從 "待定"(pending) 改變為 "成功"(fulfilled)
reject 函式的作用是將 Promise 物件的狀態從 "待定"(pending) 改變為 "失敗"(rejected), 在非同步失敗的的時候呼叫,並將非同步操作報的錯誤作為引數傳遞出去; - 可以使用 Promise 生成的例項 promise 呼叫 then() 分別指定 fulfilled狀態 和 rejected狀態 的回撥方法;
/** * promise 例項的then方法,接收兩個引數,這兩個引數都是函式 * 第一個引數:Promise物件的狀態變為 resolved 狀態時呼叫 * 第二個引數(可選的):Promise物件的狀態變為 rejected 狀態時呼叫 */ promise.then(function(value) { // value 為 Promise 物件例項化時呼叫resolve(...)方法傳入的值 // success code }, function(error) { // error 為 Promise 物件例項化時呼叫reject(...)方法傳入的錯誤 // failure code });
Promise 鏈式寫法
const promise = new Promise(function(resolve, reject) {
// 獲取一個10 以內的正整數
const num = Math.floor(Math.random()*10);
if (num > 5) {
resolve("num 大於 5");
} else {
reject("num 小於 5")
}
});
// 第一個 then
promise.then(function(value) {
// 狀態由 pending 變為 fulfilled 的回撥
// 這裡 value 的值為 (num 大於 5)
return "成功了";
}, function(error) {
// 狀態由 pending 變為 rejected 的回撥
// 這裡 error 的值為 (num 小於 5)
})
// 第二個 then
.then(function(value) {
// 這裡 value 的值為 上一個 fulfilled 狀態回撥的返回值(成功了)
console.log(value); // 如果 num > 5 列印 成功了
});
例項化一個 Promise,引數為一個函式,函式體實現隨機生成一個10以內的正整數,如果 num > 5
將 Promise 狀態改為 fulfilled 狀態,並向下傳遞了一個 "num 大於 5"
的字串,反之,將 Promise 狀態改為 rejected 狀態,向下傳遞了一個 "num 小於 5"
的字串;
當第一個 promise 的成功回撥裡返回 "成功了" 時,第二個 promise 的成功回撥的引數就是 "成功了";
由此可以看出,第一個 promise 不管成功回撥還是失敗回撥,他的返回值作為第二個 promise 中的成功時回撥函式的引數值;
鏈式寫法能一直then下去的原因:鏈式呼叫靠的是返回新的 promise,來保證可以一直走成功或失敗;
Promise.resovle
返回一個 Promise 例項,這個例項處於 resolve 狀態
根據傳入的引數不同有不同返回值:
- 值(物件, 陣列, 字串等):作為 resolve 傳遞出去的值;
- Promise 例項:原封不動返回;
// 返回一個立刻成功的promise
// 別人提供 給你一個方法,需要你傳入一個promise,但你只有一個普通的值,
// 你就可以通過這個方法把這個普通的值(string number object)轉成一個promise物件
Promise.resolve = function(value){
return new Promise(function(resolve){
resolve(value);
});
}
Promise.reject
返回一個 Promise 例項,這個例項處於 reject 狀態。
引數一般就是丟擲的錯誤資訊;
//返回一個立刻失敗的promise
Promise.reject = function(reason){
return new Promise(function(resolve,reject){
reject(reason);
});
}
Promise.catch
Promise.prototype.catch
方法是 .then(null, rejection)
的別名,用於指定發生錯誤時的回撥函式;
//catch原理就是隻傳失敗的回撥
Promise.prototype.catch = function(onRejected){
this.then(null,onRejected);
}
Promise.all
引數:接受一個數組,陣列內都是Promise例項
返回值:返回一個 Promise 例項,這個 Promise 例項的狀態轉移取決於引數的 Promise 例項的狀態變化。當引數中所有的例項都處於 resolve 狀態時,返回的 Promise 例項會變為 resolve 狀態。如果引數中任意一個例項處於 reject 狀態,返回的 Promise 例項變為 reject 狀態;
Promise.all = function(promises){
return new Promise(function(resolve,reject){
let done = gen(promises.length,resolve);
for(let i=0;i<promises.length;i++){
promises[i].then(function(data){
done(i,data);
},reject);
}
});
}
Promise.race
引數:接受一個數組,陣列內都是 Promise 例項
返回值:返回一個 Promise 例項,這個 Promise 例項的狀態轉移取決於引數的 Promise 例項的狀態變化。當引數中任何一個例項處於 resolve 狀態時,返回的 Promise 例項會變為 resolve 狀態。如果引數中任意一個例項處於 reject 狀態,返回的 Promise 例項變為 reject 狀態;
Promise.race = function(promises){
return new Promise(function(resolve,reject){
for(let i=0;i<promises.length;i++){
promises[i].then(resolve,reject);
}
});
}
總結
Promise 物件有三種狀態,並且不受外界影響,Promise 的狀態一旦改變就不會再變,並且狀態不可逆,只能從 pending(待定) 改變成 fulfilled(成功) 或 pending(待定) 改變成 rejected(失敗);
- pending(待定)
- fulfilled(成功)
- rejected(失敗)
正是因為這些特點導致 Promise 有三個缺點:
- 無法取消 Promise,一旦新建就會立即執行,中途無法取消;
- 如果沒有設定回撥函式,Promise 內部丟擲的錯誤,不會反映到外部;
- 當 Promise 處於 pending 狀態時,無法得知目前進展到哪一個階段,是剛剛開始還是即將完成;