寫在前面
感性上的理解:Promise
我們可以理解為做了一個保證,做了這個保證不管成功resolve()
還是失敗reject()
都會告知我們———返回帶有最終結果或者拒絕原因 的Promise
。
Promise的三種狀態
- 掛起
- 已成功
- 已完成
其中後兩種都是非同步操作完成後的狀態
Promise做保證
Promise
物件用於表示一個非同步操作的最終狀態(完成或失敗),以及其返回的值。
MDN對Promise的定義如上,可以理解為此物件做了一些保證,告知我們非同步操作的狀態。具體如下:
- 當前事件佇列執行完成之後,再呼叫回撥函式
- 回撥函式是通過then新增的
- 新增多個then,可以新增多個回撥函式,依次執行
Promise鏈式呼叫
存在的需求:有時候我們需要連續呼叫多個非同步操作,每一個操作都建立在得到上一部結果之後。以前有回撥函式,這樣會容易造成回撥地獄。而採用Promise之後,每一步操作成功/失敗之後都會帶上其結果,執行下一個操作。
// 回撥地獄
doSomething(function(result) {
doSomethingElse(result, function(newResult) {
doThirdThing(newResult, function(finalResult) {
console.log(`Got the final result: ` + finalResult);
}, failureCallback);
}, failureCallback);
}, failureCallback);
//採用Promise鏈式呼叫之後
doSomething().then(function(result) {
return doSomethingElse(result);
})
.then(function(newResult) {
return doThirdThing(newResult);
})
.then(function(finalResult) {
console.log(`Got the final result: ` + finalResult);
})
.catch(failureCallback);
複製程式碼
錯誤處理
在上面的程式碼段1中,有三個錯誤回撥函式。而在Promise鏈式呼叫中,只在結尾加上錯誤處理回撥即可,當有錯誤丟擲時,會返回一個帶有錯誤原因的Promise到下一級catch,直到最底端catch函式。
// 依次輸出: Initial Do that
new Promise((resolve, reject) => {
console.log(`Initial`);
resolve();
})
.then(() => {
throw new Error(`Something failed`);
console.log(`Do this`);
})
.then(() => {
console.log(`Do this whatever happened before`);
})
.catch(() => {
console.log(`Do that`);
})
複製程式碼
此外,如果中途捕獲了異常,依然可以接著then下去:
/*
* 依次輸出:
* Initial
* Do that
* Do this whatever happened before
*/
new Promise((resolve, reject) => {
console.log(`Initial`);
resolve();
})
.then(() => {
throw new Error(`Something failed`);
console.log(`Do this`);
})
.catch(() => {
console.log(`Do that`);
})
.then(() => {
console.log(`Do this whatever happened before`);
})
複製程式碼
原因在於catch(failureCallback)
本身是then(null, failureCallback)
的縮略形式,也是返回帶有當前狀態的Promise。下面這樣我們們還能捕獲到異常資訊:
/*
* 依次輸出:
* Initial
* Something failed
* Do that
* Do this whatever happened before
*/
new Promise((resolve, reject) => {
console.log(`Initial`);
resolve();
})
.then(() => {
throw new Error(`Something failed`);
console.log(`Do this`);
})
.catch((e) => {
console.log(e.message)
console.log(`Do that`);
})
.then(() => {
console.log(`Do this whatever happened before`);
})
複製程式碼
使用async/await語法糖
一個栗子
// 使用Promise
doSth()
.then(res => doSthElse(res))
.then(newRes => doThirdTh(newRes))
.then(finalRes => {
console.log(`finalResult is ${finalRes}`)
})
// 使用async/await將非同步程式碼寫成同步樣式
async funtion foo () {
let res = await doSth()
let newRes = await doSthElse(res)
let finalRes = await doThirdTh(newRes)
console.log(`finalResult is ${finalRes}`)
}
複製程式碼
Promise.resolve()、Promise.reject()妙用
使用這兩種靜態方法可以建立resolve或reject的保證,栗子如下:
getRecommend () {
let today = new Date()
let date = new Date(today.getFullYear(), today.getMonth() + 1, today.getDate(), 9)
return axios.get(`/api/getRecommend?date=${Number(date)}`
).then(response => {
return Promise.resolve(response.data)
}).catch(err => {
console.log(err)
})
}
複製程式碼
當使用axios
成功請求/api/getRecommend
時,axios返回一個Promise物件,因為getRecommend()
是要export出去的,這裡直接返回一個狀態完成的Promise,呼叫getRecommend()
時,如果成功響應直接可以recommend.getRecommend().then(res => {})
獲取響應結果。
Promise.all()、Promise.race()並行執行多個Promise物件
Promise.all()
是所有Promise物件狀態都是‘已成功’才結束Promise.race()
是有一個Promise物件狀態‘已成功’就結束
Promise序列執行
Promise鏈式呼叫
下面這個累加例子很好地顯示了Promise之間傳值計算:
// 第一步,執行XXX操作,保證了***結果
let step1 = function () {
let val = 1
console.log(`this is step1 , the value is ${val}`)
// 拒絕的情況,balalala...
if (val > 0){
return Promise.resolve(val)
}
return Promise.reject(val)
}
// 第二步,執行XXX操作,保證了***結果
let step2 = function (val) {
val += 1
console.log(`this is step2 , the value is ${val}`)
return Promise.resolve(val)
}
// 第三步,執行XXX操作,保證了***結果
let step3 = function (val) {
val += 1
console.log(`this is step3 , the value is ${val}`)
return Promise.resolve(val)
}
step1().then((val)=>{
return step2(val)
}).then((val)=>{
return step3(val)
}).then((val)=>{
console.log(`finally, the value is ${val}`)
})
// 輸出結果
this is step1 , the value is 1
this is step2 , the value is 2
this is step3 , the value is 3
複製程式碼
aysnc/await
再用async/await
語法糖實現一遍累加
// 在這裡我們把每一步操作看作非同步的
function step21() {
let val = 1
console.log(`this is step1 , the value is ${val}`)
return val
}
function step22(val) {
val += 1
console.log(`this is step2 , the value is ${val}`)
return val
}
function step23(val) {
val += 1
console.log(`this is step3 , the value is ${val}`)
return val
}
(async () => {
// await使用必須在async函式內
let val = await step21()
val = await step22(val)
val = await step23(val)
})()
// 輸出結果
this is step1 , the value is 1
this is step2 , the value is 2
this is step3 , the value is 3
複製程式碼
Promise陣列序列
使用陣列的reduce()
方法依次執行,
// 第一步,執行XXX操作,保證了***結果
let step1 = function () {
let val = 1
console.log(`this is step1 , the value is ${val}`)
// 拒絕的情況,balalala...
if (val > 0){
return Promise.resolve(val)
}
return Promise.reject(val)
}
// 第二步,執行XXX操作,保證了***結果
let step2 = function (val) {
val += 1
console.log(`this is step2 , the value is ${val}`)
return Promise.resolve(val)
}
// 第三步,執行XXX操作,保證了***結果
let step3 = function (val) {
val += 1
console.log(`this is step3 , the value is ${val}`)
return Promise.resolve(val)
}
let steps = [step1, step2, step3]
steps.reduce((promise, fn, index)=>{
console.log(index)
return promise.then((val)=>{
return fn(val)
})
// reduce函式init引數
}, Promise.resolve())
// 輸出結果
this is step1 , the value is 1
this is step2 , the value is 2
this is step3 , the value is 3
複製程式碼
async/await陣列序列
暫時只想到陣列的方法,使用Array.prptotype.reduce()
為解決,還是沒有深入瞭解reduce()
是如何實現的
async function foo () {
let val
for (let i = 0; i < steps.length; i++) {
if (i===0) {
val = await steps[i]()
}else {
val = await steps[i](val)
}
}
}
foo()
複製程式碼