1. 程式人生 > 其它 >promise和async

promise和async

promise

  • Promise,我們瞭解到promise是ES6為解決非同步回撥而生,避免出現這種回撥地獄,那麼為何又需要Async/Await呢?你是不是和我一樣對Async/Await感興趣以及想知道如何使用,下面一起來看看這篇文章:Async/Await替代Promise的6個理由。

  • promise的核心原理其實就是釋出訂閱模式,通過兩個佇列來快取成功的回撥(onResolve)和失敗的回撥(onReject)。如果還不熟悉promise用法的朋友,請參考Es6入門之promise物件,下面來分析promise的特點。

promise基礎版實現

new Promise時需要傳遞一個executor執行器,執行器會立刻執行;執行器中傳遞了兩個引數:resolve成功的函式、reject失敗的函式,他們呼叫時可以接受任何值的引數value

function Promise (executor){
    function resolve(value){}
    function reject(value){}
    try{
        executor(resolve,reject);
    }catch(e){
        reject(e);
    }
}
var promise = new Promise((resolve,reject)=>{
    console.log('start');
})

promise完整程式碼

 // 判斷變數否為function
  const isFunction = variable => typeof variable === 'function'
  // 定義Promise的三種狀態常量
  const PENDING = 'PENDING'
  const FULFILLED = 'FULFILLED'
  const REJECTED = 'REJECTED'

  class MyPromise {
    constructor (handle) {
      if (!isFunction(handle)) {
        throw new Error('MyPromise must accept a function as a parameter')
      }
      // 新增狀態
      this._status = PENDING
      // 新增狀態
      this._value = undefined
      // 新增成功回撥函式佇列
      this._fulfilledQueues = []
      // 新增失敗回撥函式佇列
      this._rejectedQueues = []
      // 執行handle
      try {
        handle(this._resolve.bind(this), this._reject.bind(this)) 
      } catch (err) {
        this._reject(err)
      }
    }
    // 新增resovle時執行的函式
    _resolve (val) {
      const run = () => {
        if (this._status !== PENDING) return
        this._status = FULFILLED
        // 依次執行成功佇列中的函式,並清空佇列
        const runFulfilled = (value) => {
          let cb;
          while (cb = this._fulfilledQueues.shift()) {
            cb(value)
          }
        }
        // 依次執行失敗佇列中的函式,並清空佇列
        const runRejected = (error) => {
          let cb;
          while (cb = this._rejectedQueues.shift()) {
            cb(error)
          }
        }
        /* 如果resolve的引數為Promise物件,則必須等待該Promise物件狀態改變後,
          當前Promsie的狀態才會改變,且狀態取決於引數Promsie物件的狀態
        */
        if (val instanceof MyPromise) {
          val.then(value => {
            this._value = value
            runFulfilled(value)
          }, err => {
            this._value = err
            runRejected(err)
          })
        } else {
          this._value = val
          runFulfilled(val)
        }
      }
      // 為了支援同步的Promise,這裡採用非同步呼叫
      setTimeout(run, 0)
    }
    // 新增reject時執行的函式
    _reject (err) { 
      if (this._status !== PENDING) return
      // 依次執行失敗佇列中的函式,並清空佇列
      const run = () => {
        this._status = REJECTED
        this._value = err
        let cb;
        while (cb = this._rejectedQueues.shift()) {
          cb(err)
        }
      }
      // 為了支援同步的Promise,這裡採用非同步呼叫
      setTimeout(run, 0)
    }
    // 新增then方法
    then (onFulfilled, onRejected) {
      const { _value, _status } = this
      // 返回一個新的Promise物件
      return new MyPromise((onFulfilledNext, onRejectedNext) => {
        // 封裝一個成功時執行的函式
        let fulfilled = value => {
          try {
            if (!isFunction(onFulfilled)) {
              onFulfilledNext(value)
            } else {
              let res =  onFulfilled(value);
              if (res instanceof MyPromise) {
                // 如果當前回撥函式返回MyPromise物件,必須等待其狀態改變後在執行下一個回撥
                res.then(onFulfilledNext, onRejectedNext)
              } else {
                //否則會將返回結果直接作為引數,傳入下一個then的回撥函式,並立即執行下一個then的回撥函式
                onFulfilledNext(res)
              }
            }
          } catch (err) {
            // 如果函式執行出錯,新的Promise物件的狀態為失敗
            onRejectedNext(err)
          }
        }
        // 封裝一個失敗時執行的函式
        let rejected = error => {
          try {
            if (!isFunction(onRejected)) {
              onRejectedNext(error)
            } else {
                let res = onRejected(error);
                if (res instanceof MyPromise) {
                  // 如果當前回撥函式返回MyPromise物件,必須等待其狀態改變後在執行下一個回撥
                  res.then(onFulfilledNext, onRejectedNext)
                } else {
                  //否則會將返回結果直接作為引數,傳入下一個then的回撥函式,並立即執行下一個then的回撥函式
                  onFulfilledNext(res)
                }
            }
          } catch (err) {
            // 如果函式執行出錯,新的Promise物件的狀態為失敗
            onRejectedNext(err)
          }
        }
        switch (_status) {
          // 當狀態為pending時,將then方法回撥函式加入執行佇列等待執行
          case PENDING:
            this._fulfilledQueues.push(fulfilled)
            this._rejectedQueues.push(rejected)
            break
          // 當狀態已經改變時,立即執行對應的回撥函式
          case FULFILLED:
            fulfilled(_value)
            break
          case REJECTED:
            rejected(_value)
            break
        }
      })
    }
    // 新增catch方法
    catch (onRejected) {
      return this.then(undefined, onRejected)
    }
    // 新增靜態resolve方法
    static resolve (value) {
      // 如果引數是MyPromise例項,直接返回這個例項
      if (value instanceof MyPromise) return value
      return new MyPromise(resolve => resolve(value))
    }
    // 新增靜態reject方法
    static reject (value) {
      return new MyPromise((resolve ,reject) => reject(value))
    }
    // 新增靜態all方法
    static all (list) {
      return new MyPromise((resolve, reject) => {
        /**
         * 返回值的集合
         */
        let values = []
        let count = 0
        for (let [i, p] of list.entries()) {
          // 陣列引數如果不是MyPromise例項,先呼叫MyPromise.resolve
          this.resolve(p).then(res => {
            values[i] = res
            count++
            // 所有狀態都變成fulfilled時返回的MyPromise狀態就變成fulfilled
            if (count === list.length) resolve(values)
          }, err => {
            // 有一個被rejected時返回的MyPromise狀態就變成rejected
            reject(err)
          })
        }
      })
    }
    // 新增靜態race方法
    static race (list) {
      return new MyPromise((resolve, reject) => {
        for (let p of list) {
          // 只要有一個例項率先改變狀態,新的MyPromise的狀態就跟著改變
          this.resolve(p).then(res => {
            resolve(res)
          }, err => {
            reject(err)
          })
        }
      })
    }
    finally (cb) {
      return this.then(
        value  => MyPromise.resolve(cb()).then(() => value),
        reason => MyPromise.resolve(cb()).then(() => { throw reason })
      );
    }
  }

什麼是Async/Await

  • async/await是寫非同步程式碼的新方式,使用的方式看起來像同步,以前的方法有回撥函式和Promise。
  • async/await是基於Promise實現的,它不能用於普通的回撥函式。

Async/Await語法

使用Promise是這樣的:

const makeRequest = () =>
  getJSON()
    .then(data => {
      console.log(data)
      return "done"
    })

makeRequest()
  • 示例中,getJSON函式返回一個promise,這個promise成功resolve時會返回一個JSON物件。我們只是呼叫這個函式,列印返回的JSON物件,然後返回”done”。

使用Async/Await是這樣的:

const makeRequest = async () => {
  console.log(await getJSON())
  return "done"
}

makeRequest()
它們有一些細微不同:
  • 函式前面多了一個aync關鍵字。await關鍵字只能用在aync定義的函式內。async函式會隱式地返回一個promise,該promise的reosolve值就是函式return的值。(示例中reosolve值就是字串”done”)
  • 第1點暗示我們不能在最外層程式碼中使用await,因為不在async函式內。

為什麼Async/Await更好?

1. 簡潔

  • 由示例可知,使用Async/Await明顯節約了不少程式碼。我們不需要寫.then,不需要寫匿名函式處理Promise的resolve值,也不需要定義多餘的data變數,還避免了巢狀程式碼。這些小的優點會迅速累計起來,這在之後的程式碼示例中會更加明顯。

2.錯誤處理

Async/Await讓try/catch可以同時處理同步和非同步錯誤。在下面的promise示例中,try/catch不能處理JSON.parse的錯誤,因為它在Promise中。我們需要使用.catch,這樣錯誤處理程式碼非常冗餘。並且,在我們的實際生產程式碼會更加複雜。

const makeRequest = () => {
  try {
    getJSON()
      .then(result => {
        // JSON.parse可能會出錯
        const data = JSON.parse(result)
        console.log(data)
      })
      // 取消註釋,處理非同步程式碼的錯誤
      // .catch((err) => {
      //   console.log(err)
      // })
  } catch (err) {
    console.log(err)
  }
}

  • 使用aync/await的話,catch能處理JSON.parse錯誤:
const makeRequest = async () => {
  try {
    // this parse may fail
    const data = JSON.parse(await getJSON())
    console.log(data)
  } catch (err) {
    console.log(err)
  }
}

3.條件語句

  • 下面示例中,需要獲取資料,然後根據返回資料決定是直接返回,還是繼續獲取更多的資料。
// promise
const makeRequest = () => {
  return getJSON()
    .then(data => {
      if (data.needsAnotherRequest) {
        return makeAnotherRequest(data)
          .then(moreData => {
            console.log(moreData)
            return moreData
          })
      } else {
        console.log(data)
        return data
      }
    })
}

  • 上面的程式碼使用async/await編寫可以大大地提高可讀性:
const makeRequest = async () => {
  const data = await getJSON()
  if (data.needsAnotherRequest) {
    const moreData = await makeAnotherRequest(data);
    console.log(moreData)
    return moreData
  } else {
    console.log(data)
    return data    
  }
}

4. 除錯

promise
  • promise不能在返回表示式的箭頭函式中設定斷點
  • 如果你在.then程式碼塊中設定斷點,使用Step Over快捷鍵,偵錯程式不會跳到下一個.then,因為它只會跳過非同步程式碼
async
  • async/await能夠使得程式碼除錯更簡單
  • 使用await/async時,你不再需要那麼多箭頭函式,這樣你就可以像除錯同步程式碼一樣跳過await語句
  • 如果它等到的不是一個 Promise 物件,那 await 表示式的運算結果就是它等到的東西。
  • 如果它等到的是一個 Promise 物件,await 就忙起來了,它會阻塞後面的程式碼,等著 Promise 物件 resolve,然後得到 resolve 的值,作為 await 表示式的運算結果。