【筆記】再學JavaScript ES(6-10)全版本語法——回調地獄&異步操作(Promise/then/resolve/reject/catch/all/race)


一、callback

回調地獄:回調函數需要依賴上一層的回調執行完,所以形成了層層嵌套的關係最終形成回調地獄,無疑是不利於閱讀與維護的一種形式。

{
  let catList = ''
  function f1 (name) {
    catList += name + ' '
  }
  f1('My', f1('name', f1('is', f1('oliver'))))
  console.log(catList) // oliver is name My
}

二、Promise

使用Promise對象封裝一個或多個異步操作,它提供統一的多種API,調用方便,它有如下特點:

  • 對象的狀態不受外界影響,只由異步操作的結果決定。一個Promise對象代表一個異步操作,每個異步操作有三種狀態:pending(進行中)、fulfilled(已成功)和rejected(已失敗)。
  • 一旦異步操作有了結果,狀態就不會再變。Promise對象的狀態改變,只有兩種可能:從pending變爲fulfilled和從pending變爲rejected。

1.then

{
  let catList = ''
  function f3 (name) {
    return new Promise((resolve, reject) => {
      catList += name + ' '
      resolve(catList)
      reject(new Error('error!'))
    })
  }
  f3('My').then(
    f3(`name`)
  ).then(
    f3('is')
  ).then(
    f3('oliver')
  )
  console.log(catList) // My name is oliver
  catList = ''
  /**
   * 關於.then的完善
   */
  // 完善:成功回調和失敗回調
  f3('My').then(() => {
    f3(`name`)
  }, err => {
    console.log(err)
  }).then(() => {
    f3('is')
  }, err => {
    console.log(err)
  }).then(() => {
    f3('oliver')
  }, err => {
    console.log(err)
  })
  console.log(catList) // My name is oliver
  catList = ''
  // 完善:發生錯誤前(在試錯階段:不試錯怎麼能知道需要執行錯誤回調?)仍然生成空白Promise對象
  f3('My').then(() => {
    return f3(`name`)
  }, (err) => {
    console.log(err)
  }).then(() => {
    return f3('is')
  }, (err) => {
    console.log(err)
  }).then(() => {
    return f3('oliver')
  }, (err) => {
    console.log(err)
  })
  console.log(catList) // My name is oliver
  catList = ''
  // 完善:使用catch統一處理錯誤
  f3('My').then(() => {
    return f3(`name`)
  }).then(() => {
    return f3('is')
  }).then(() => {
    return f3('oliver')
  }).catch(err => {
    console.log(err)
  })
  console.log(catList) // My name is oliver
}

Promise.prototype.then(onFulfilled, onRejected)是Promise對象原型上的方法(實例方法)

  • onFulfilled是resolved狀態的回調函數,必選
  • onRejected是rejected狀態的回調函數,可選
  • 兩個參數都是函數類型
  • 調用宿主是Promise對象

2.catch

場景見上個示例代碼中

f3('My').then(() => {
   return f3(`name`)
 }).then(() => {
   return f3('is')
 }).then(() => {
   return f3('oliver')
 }).catch(err => {
   console.log(err)
 })

Promise.prototype.catch是Promise對象原型上的方法(實例方法)

  • 這裏的catch捕獲的是改變Promise狀態爲reject的錯誤,不能用來捕獲throw的錯誤

3.resolve & reject

/**
 * 普通方法中使用Promise實現異步回調
 * @param flag
 */
function test (flag) {
  if (flag) {
    return new Promise((resolve, reject) => {
      resolve('success!')
      reject(new Error('error!'))
    })
  } else {
    // 重點在這裏:這裏也必須要返回一個Promise對象否則調用時不能使用then, 可直接調用Promise靜態方法
    return Promise.reject(new Error('error!'))
  }
}
test(1).then((value) => {
  console.log(value)
}, (err) => {
  console.log(err)
})

resolve 和 reject 都是 Promise 對象的靜態方法,需要通過Promise直接調用

4.all

場景:從三個接口請求數據然後數據聚合,此時並不知道哪個接口速度快,也就是請求不分先後,非鏈式調用Promise對象

{
  const p1 = Promise.resolve(1)
  const p2 = Promise.resolve(2)
  const p3 = Promise.resolve(3)

  Promise.all([p1, p2, p3]).then(value => {
    console.log(value)
  })
}

(1)只有p1、p2、p3的狀態都變成fulfilled,Promise的狀態纔會變成fulfilled,此時p1、p2、p3的返回值組成一個數組,傳遞給Promise的回調函數。
(2)只要p1、p2、p3之中有一個被rejected,Promise的狀態就變成rejected,此時第一個被reject的實例的返回值,會傳遞給Promise的回調函數。

5.race

場景:一張網站重要圖片存放於三個CDN中,調用時

{
  const p1 = () => {
    return new Promise((resolve) => {
      setTimeout(() => {
        resolve(1)
      }, 1001)
    })
  }
  const p2 = () => {
    return new Promise((resolve) => {
      setTimeout(() => {
        resolve(2)
      }, 1000)
    })
  }
  const p3 = () => {
    return new Promise((resolve) => {
      setTimeout(() => {
        resolve(3)
      }, 999)
    })
  }
  Promise.race([p1(), p2(), p3()]).then(value => {
    console.log(value) // 3
  })
}

只要p1、p2、p3之中有一個實例率先改變狀態,Promise的狀態就跟着改變。那個率先改變的 Promise 實例的返回值,就傳遞給Promise的回調函數

拓展:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章