最近由於對 promise 的狀態依賴理解有誤差,致使在開發過程當中花費了3個小時的時間才發現錯誤。感受浪費時間了,因此結合標準及實踐結果對 promise 的狀態依賴作了一個總結。javascript
問題代碼大體是這樣的:java
// 假設promise爲請求錯誤回調
let promise = new Promise((resolve, reject) => {
reject('400');
});
// 統一的響應攔截處理
promise.then((res) => {
console.log('promise injector resolved', res)
}, (err) => {
console.log('promise injector rejected', err)
})
// 請求調用處的業務響應處理
.then((res) => {
console.log('promise resolved', res)
}, (err) => {
console.log('promise rejected', err)
})
複製代碼
上面代碼中表現的場景是,錯誤請求通過請求響應攔截器的統一處理後,業務邏輯自己再根據請求狀態來進行一些其餘的處理。原本按照我對promise的理解,這樣是沒有問題的。實際上這裏的問題是,業務中的請求響應處理永遠只會走成功回調,而不會走失敗回調。由於在進行統一的響應攔截處理的時候,就已經丟失了 promise 的狀態了。promise
promise共有是三種狀態:異步
這三種狀態是 Promises/A+ 中描述狀態的術語函數
當咱們建立一個 promise 時,它的狀態是 Pending,而後隨着異步任務的執行,它的狀態必定會變成 Fulfilled 和 Rejected 中的一種,且它的狀態不會再發生任何變化(這一點很重要,後面捋清楚 promise.then() 的返回值就靠這個特性了)。ui
先來一代碼:spa
let promise = Promise.resolve('test');
let thenPromise = promise.then( res =>{
console.log(res)
});
console.log(promise === thenPromise);
console.log(thenPromise);
複製代碼
運行結果以下: 3d
從運行結果中咱們不可貴出兩點:code
promise.then() 返回的是一個新的 Promise 的實例對象,而 promise 是可以能表示狀態的,這樣就能夠造成一條狀態的依賴鏈。也就能將多個任務之間的依賴及執行順序表示出來了,從而將異步任務的回調嵌套轉化成爲一條扁平的鏈條。cdn
promise chain 說白了就是個 promise 的調用鏈,代碼形式大體以下:
var promise1 = Promise.reject('test');
promise1.then(function(res) {
console.log('fulfilled1', res)
}, function(err) {
console.log('rejected1', err)
return err;
}).then(function(res) {
console.log('fulfilled2', res)
}, function(err) {
console.log('rejected2', err)
})
複製代碼
這樣的鏈式調用方式,很好的將多個存在依賴關係的異步任務,將難看回調嵌套轉化成了一條更易於理解的扁平鏈條。從而解決了所謂回調地獄的問題。
當咱們建立了一個 promise 以後,就可使用 promise.then() 來註冊,Fulfilled 和 Rejected 狀態對應的執行函數了。相似下面這樣:
var promise = new Promise((resolve, reject)=>{
resolve('ok');
});
promise.then((res)=>{
console.log('fulfilled', res)
}, (err)=>{
console.log('rejected', err)
})
複製代碼
promise.then() 註冊的回調函數能夠返回不一樣的值,分爲如下兩種:
promise.then() 回調函數的返回值決定了其返回的 promise,大體分爲如下兩種情形:
注意: 這裏的回調函數指的是 promise.then() 註冊的回調函數,且是指的是被調用的函數,與是成功回調仍是失敗回調無關
當 promise.then() 註冊的回調函數返回除promise之外的值時,返回的值會被當作 promise.then() 返回的新的 promise 使用 .then() 註冊的回調函數的傳入值。
當回調函數未返回值時,考慮下面代碼:
let promise = Promise.resolve();
promise.then(()=>{
console.log('promise resolved')
}, ()=>{
console.log('promise rejected')
})
.then((value)=>{
console.log('promise resolved1')
console.log(value)
}, (err)=>{
console.log('promise rejected1')
console.log(err)
})
// promise resolved
// promise resolved1
// undefined
let promise1 = Promise.reject();
promise1.then(()=>{
console.log('promise1 resolved')
}, ()=>{
console.log('promise1 rejected')
})
.then((value)=>{
console.log('promise1 resolved1')
console.log(value)
}, (err)=>{
console.log('promise1 rejected1')
console.log(err)
})
// promise1 rejected
// promise1 resolved1
// undefined
複製代碼
promise 註冊的回調運行了成功回調,然後返回了一個成功狀態的promise,這個返回的 promise 註冊的回調運行了成功回調,回調函數的實參是 undefined;promise1 註冊的回調運行了失敗回調,然後一樣返回了一個成功狀態的 promise ,這個返回的 promise 註冊的回調運行了成功回調,回調函數的實參是 undefined
js 函數執行後默認返回值爲undefined
再來看看返回其餘值的狀況:
let promise = Promise.resolve();
promise.then(()=>{
console.log('promise resolved')
return 'value'
}, ()=>{
console.log('promise rejected')
return 'err'
})
.then((value)=>{
console.log('promise resolved1')
console.log(value)
}, (err)=>{
console.log('promise rejected1')
console.log(err)
})
// promise resolved
// promise resolved1
// value
let promise1 = Promise.reject();
promise1.then(()=>{
console.log('promise1 resolved')
return 'value'
}, ()=>{
console.log('promise1 rejected')
return 'err'
})
.then((value)=>{
console.log('promise1 resolved1')
console.log(value)
}, (err)=>{
console.log('promise1 rejected1')
console.log(err)
})
// promise1 rejected
// promise1 resolved1
// err
複製代碼
promise.then() 註冊的回調函數,運行的是成功回調,且返回了字符串 「value」,promise.then() 函數返回了一個成功狀態的 promise, 返回的 promise 註冊的回調函數運行了成功回調,回調函數的實參是字符串「value」;promise1.then() 註冊的回調函數,運行的是失敗回調,且返回了字符串 「err」,promise1.then() 函數返回了一個成功狀態的 promise, 返回的 promise 註冊的回調函數運行了成功回調,回調函數的實參是字符串「err」
從上面兩段示例代碼及其運行結果,不可貴出以下結論:
當 promise.then() 註冊的被運行的回調函數返回一個 Promise 實例的時候,回調返回的 Promise 實例的最終的狀態會決定 promise.then() 函數返回的這個新的 promise 的狀態。且promise.then() 函數返回的 promise 註冊的回調函數的實參是由 Promise 實例對象 resolve 或 reject 傳遞的參數決定的。
考慮以下代碼:
let promiseValue = Promise.reject('promiseValue err');
let promise = Promise.resolve();
let promiseThen = promise.then(()=>{
console.log('promise resolved');
return promiseValue;
})
promiseThen.then((res)=>{
console.log('promise resolved1');
console.log(res)
}, (err)=>{
console.log('promise rejected1');
console.log(err);
})
// promise resolved
// promise rejected1
// promiseValue err
console.log(promiseThen === promiseValue)
// false
複製代碼
promise 執行成功狀態回調,回調函數返回失敗狀態的 Promise 實例對象 promiseValue,且其 rejecte 傳入的值是字符串 「promiseValue err」。由於 promiseValue 的狀態是 rejected 因此 promise.then() 返回的新的 Promise 實例(promiseThen === promiseValue 的結果是 false 證實promise.then()返回的)promiseThen 的狀態是 rejected,promiseThen 註冊的失敗回調被調用,打印的實參爲字符串「promiseValue err」。
promise.then() 註冊的被執行的回調函數返回 Promise 實例對象時:
正是由於 promise.then() 返回的是一個新的 Promise 實例,且這個實例能夠向後傳遞上一個依賴 promise 的異步任務的執行狀態和其要傳遞的數據。這樣的鏈式調用機制,解決了存在依賴關係的異步任務只能在回調函數中不斷嵌套的最後致使代碼難以維護的問題。