解決程式碼中重複的捕獲 promise 錯誤的 try catch 語句

千水xxx發表於2018-12-17

promise

promise 的出現,提供了優雅的非同步解決方式,但是,多個連續繼發 promise 寫法依然繁瑣。

let promise = new Promise(function(resolve, reject){
  // ...
  if(/* 非同步任務執行成功 */) {
    resolve(value)
  } else {
    reject(error)
  }
})

promise.then(v => {}).catch(e => {})
複製程式碼

async

es6 之後又新增了 async 函式來優化非同步寫法,語義化更明確,寫法更優雅,但是錯誤捕獲比較麻煩,一般都得使用 try catch 來捕獲錯誤,具體優點參考阮老師部落格 async 函式

function promiseFunc = function(){
  return new Promise(function(resolve, reject){
    // ...
    if(/* 非同步任務執行成功 */) {
      resolve(value)
    } else {
      reject(error)
    }
  })
}

async func(){
  let res = await promiseFunc()
}

// 錯誤捕獲
async func(){
  try{
    let res = await promiseFunc()
  }catch(err){
    alert(err)
  }
}

複製程式碼

錯誤捕獲優化

如下是工作中 react + mobx 專案中 action 的程式碼

class Actions {
  @action
  async deleteModel(params) {
    try {
      await this.post(apis.API_DEPLOY_DELETE, params)
      this.getDeployList(this.store.searchParams)
    } catch (e) {
      message.error(e.message || '出錯了!請稍後重試')
    }
  }

  @action
  async getDirChain(params) {
    try {
      let r = await this.post(apis.API_DEPLOY_DIR_CHAIN, params)
      runInAction(() => {
        this.store.dirChain = r
      })
    } catch (e) {
      message.error(e.message || '出錯了!請稍後重試')
    }
  }

}
複製程式碼

如上程式碼,兩個 action 都是向後端非同步請求資料, 每個 action 函式中都用了 try catch 函式,這樣重複寫了幾十個 action 函式

必須幹掉 try catch

錯誤捕獲裝飾器嘗試

裝飾器簡潔方便,首先嚐試, class 方法裝飾器函式如下

const tryCatch = msg => (target, name, descriptor) => {
  const original = descriptor.value
  if (typeof original === 'function') {
    descriptor.value = async function(...args) {
      try {
        const result = await original.apply(this, args)
        return result
      } catch (e) {
        message.error(e.message || msg || '出錯了!請稍後重試')
      }
    }
  }
  return descriptor
}
複製程式碼

如上程式碼,封裝 tryCatch 裝飾器來對每個 action 函式新增 try catch 錯誤捕獲。

屬性方法裝飾器中

  • target 指向 class 例項
  • name 是被裝飾的方法名
  • descriptor 是方法的屬性修飾符

我們可以通過 descriptor.value 獲取到被裝飾的方法,在 try catch 中執行函式,捕獲錯誤或者返回結果

為了靈活提示錯誤資訊,裝飾器引數 msg 用來傳入自定義提示文字

  • 用法(該用法是錯誤的)
@tryCatch // @tryCatch('執行出錯')
@action
async getDirChain(params) {
  let r = await this.post(apis.API_DEPLOY_DIR_CHAIN, params)
  runInAction(() => {
    this.store.dirChain = r
  })
}
複製程式碼

以上對 async 函式進行錯誤捕獲的用法是錯誤的

如上的寫法是錯誤的,這種裝飾器只能對同步程式碼產生作用,非同步的是無效的,之前理解錯誤了

最後還是解決了 try catch 問題

直接 async await 函式封裝就行了,簡單的問題想複雜了。。。

定義請求執行公共函式

/**
 * @param {string} method request method; ('get', 'post')
 * @param {string} api request url
 * @param {object} params payload
 * @memberof BaseActions
 */
request = async (method, api, params = {}) => {
  const requestFunc = async () => {
    let r = null
    try {
      r = await this[method](api, params)
    } catch (e) {
      message.error(e.message)
    }
    return r
  }

  return await requestFunc()
}
複製程式碼

原有包含 try catch 重複程式碼函式修改

@action
async getDirChain(params) {
  let r = await this.request('get', apis.API_DEPLOY_DIR_CHAIN, params)
  r && runInAction(() => this.store.dirChain = r)
}
複製程式碼

終於不用不停寫重複程式碼了。。。

相關文章