解決程式碼中重複的捕獲 promise 錯誤的 try catch 語句
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) } 複製程式碼
終於不用不停寫重複程式碼了。。。