潛入理解ES6-Promise用法小結

noopyxu發表於2019-01-24

寫在前面

感性上的理解: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()
複製程式碼

相關文章