如何优雅处理 async await 错误——解读小而美的 await-to-js 库
大家好,我是若川。持續(xù)組織了5個月源碼共讀活動,感興趣的可以點此加我微信 ruochuan12?參與,每周大家一起學(xué)習(xí)200行左右的源碼,共同進(jìn)步。同時極力推薦訂閱我寫的《學(xué)習(xí)源碼整體架構(gòu)系列》?包含20余篇源碼文章。
這是源碼共讀活動第21期 await-to-js,優(yōu)雅的處理 async await 的 try-catch,讀者掘金@愛嘿嘿的小黑 的投稿。
1前言
學(xué)而不思則罔
最近有在讀一些比較優(yōu)秀的npm包的代碼,起因是感覺自己現(xiàn)在寫的代碼還是不夠規(guī)范,不夠簡潔。
可是我又不知道到底什么樣的代碼才算是比較好的代碼,在進(jìn)行一番思考過后我認(rèn)為還是要站在巨人的肩膀上。
通過閱讀優(yōu)秀的源碼并從中學(xué)習(xí)如何寫出讓人覺得賞心悅目的代碼最后再寫文進(jìn)行章總結(jié)對整個學(xué)習(xí)的過程進(jìn)行一個梳理同時分享給其他人。
為什么要在開頭寫這么多呢?因為我需要為自己堅持下去找一個理由。這樣我才能乘風(fēng)破浪,一往無前。
話不多說,開始總結(jié)。
2JS異步編程進(jìn)化之路
回調(diào)地獄階段
在正式介紹await-to-js這個庫之前,讓我們先簡單的回顧一下有關(guān)于在JavaScript這門語言中,異步編程的進(jìn)化之路。在Promise沒出現(xiàn)之前,異步編程一直是困擾著前端工程師的一個大難題,當(dāng)時的前輩可能會經(jīng)常看到下面這種代碼。
function?AsyncTask()?{asyncFuncA(function(err,?resultA){if(err)?return?cb(err);asyncFuncB(function(err,?resultB){if(err)?return?cb(err);asyncFuncC(function(err,?resultC){if(err)?return?cb(err);//?And?so?it?goes....});});}); }這種同時在縱向和橫向延伸的回調(diào)中嵌套著回調(diào)的代碼又被稱為回調(diào)地獄。可見這玩意讓人多么惡心,具體來說有以下這幾個缺點
難以維護(hù)(看都不想看,還維護(hù)個**)
難以捕捉到錯誤(一個一個找?) 總而言之,這個問題在當(dāng)時是很需要被解決的,所以在ES6中,出現(xiàn)了Promise。
Promise階段
Promise是一種優(yōu)雅的異步編程解決方案。從語法上來將,它是一個對象, 代表著一個異步操作最終完成或失敗,從語意上來講,它是承諾,承諾過一段時間給你一個結(jié)果。
由于它的原型存在then,catch,finally會返回一個新的promise所以可以允許我們鏈?zhǔn)秸{(diào)用,解決了傳統(tǒng)的回調(diào)地獄的問題。
由于它本身存在all方法,所以可以支持多個并發(fā)請求,獲取并發(fā)請求中數(shù)據(jù)。
有了Promise后,上面的代碼可以被寫成下面這樣。
function?asyncTask(cb)?{asyncFuncA.then(AsyncFuncB).then(AsyncFuncC).then(AsyncFuncD).then(data?=>?cb(null,?data).catch(err?=>?cb(err)); }相比較于上面的回調(diào)地獄,使用Promise可以幫助我們讓代碼只在縱向發(fā)展,并且提供了處理錯誤的回調(diào)。顯然優(yōu)雅了很多。不過就算Promise已經(jīng)這么優(yōu)秀了,可是依然存在兩個每種不足的地方
不夠同步(代碼依然會縱向延伸)
不能給每一次異步操作都進(jìn)行錯誤處理 這也就是為什么ES7中會出現(xiàn)async/await,號稱異步編程的最后解決方案的原因了。
async/await
async 函數(shù)是 Generator 函數(shù)的語法糖。使用 關(guān)鍵字 async 來表示,在函數(shù)內(nèi)部使用 await 來表示異步。相較于 Generator,async 函數(shù)的改進(jìn)在于下面四點:
內(nèi)置執(zhí)行器。Generator 函數(shù)的執(zhí)行必須依靠執(zhí)行器,而 async 函數(shù)自帶執(zhí)行器,調(diào)用方式跟普通函數(shù)的調(diào)用一樣
更好的語義。async 和 await 相較于 * 和 yield 更加語義化
更廣的適用性。co 模塊約定,yield 命令后面只能是 Thunk 函數(shù)或 Promise對象。而 async 函數(shù)的 await 命令后面則可以是 Promise 或者 原始類型的值(Number,string,boolean,但這時等同于同步操作)
返回值是 Promise。async 函數(shù)返回值是 Promise 對象,比 Generator 函數(shù)返回的 Iterator 對象方便,可以直接使用 then() 方法進(jìn)行調(diào)用
此處總結(jié)參考自:理解async/await[1]
有了async/await,上面的代碼可以被改寫成下面這樣
function?async?asyncTask(cb)?{const?asyncFuncARes?=?await?asyncFuncA()const?asyncFuncBRes?=?await?asyncFuncB(asyncFuncARes)const?asyncFuncCRes?=?await?asyncFuncC(asyncFuncBRes) }同時我們可以對每一次異步操作進(jìn)行錯誤處理
function?async?asyncTask(cb)?{try?{const?asyncFuncARes?=?await?asyncFuncA()}?catch(error)?{return?new?Error(error)}try?{const?asyncFuncBRes?=?await?asyncFuncB(asyncFuncARes)}?catch(error)?{return?new?Error(error)}try?{const?asyncFuncCRes?=?await?asyncFuncC(asyncFuncBRes)}?catch(error)?{return?new?Error(error)} }這樣一來上面Promise存在的兩個每種不足的地方是不是就被優(yōu)化了呢?所以說async/await是JS中異步編寫的最后解決方案我個人覺得一點問題沒有,但是我不知道你看上面的代碼,每一次異步操作都要用try/catch進(jìn)行錯誤處理是不是感覺不夠方便不夠智能呢?
3await-to-js-小而美的npm包
基本用法
作者是這樣介紹這個庫的
Async await wrapper for easy error handling without try-catch。
中文翻譯過來就是
無需 try-catch 即可輕松處理錯誤的異步等待包裝器。
這里做個簡單的對比,之前我們在異步操作中處理錯誤的方法是這樣的
function?async?asyncTask()?{try?{const?asyncFuncARes?=?await?asyncFuncA()}?catch(error)?{return?new?Error(error)}try?{const?asyncFuncBRes?=?await?asyncFuncB(asyncFuncARes)}?catch(error)?{return?new?Error(error)}try?{const?asyncFuncCRes?=?await?asyncFuncC(asyncFuncBRes)}?catch(error)?{return?new?Error(error)} }而用了await-to-js之后,我們可以這樣的處理錯誤
import?to?from?'./to.js'; function?async?asyncTask()?{const?[err,?asyncFuncARes]??=?await?to(asyncFuncA())if(err)?throw?new?(error);const?[err,?asyncFuncBRes]??=?await?tp(asyncFuncB(asyncFuncARes))if(err)?throw?new?(error);const?[err,?asyncFuncCRes]??=?await?to(asyncFuncC(asyncFuncBRes)if(err)?throw?new?(error); }是不是簡潔多了呢?
作者究竟用了什么黑魔法?
你可能不信,源碼只有僅僅15行。
源碼分析
export?function?to<T,?U?=?Error>?(promise:?Promise<T>,errorExt?:?object ):?Promise<[U,?undefined]?|?[null,?T]>?{return?promise.then<[null,?T]>((data:?T)?=>?[null,?data]).catch<[U,?undefined]>((err:?U)?=>?{if?(errorExt)?{const?parsedError?=?Object.assign({},?err,?errorExt);return?[parsedError,?undefined];}return?[err,?undefined];}); }export?default?to;上面這里是TS版的源碼,但是考慮到有些同學(xué)可能還沒接觸過TS,我著重分析一下下面這版JS版的源碼。
export?function?to(promise,?errorExt)?{return?promise.then((data)?=>?[null,?data]).catch((err)?=>?{if?(errorExt)?{const?parsedError?=?Object.assign({},?err,?errorExt);return?[parsedError,?undefined];}return?[err,?undefined];}); } export?default?to;這里我們先拋開errorExt這個自定義的錯誤文本,核心代碼是這樣的
export?function?to(promise)?{return?promise.then((data)?=>?[null,?data])?//?成功,返回[null,響應(yīng)結(jié)果].catch((err)?=>?{return?[err,?undefined];?//?失敗,返回[錯誤信息,undefined]}); } export?default?to;可以看出,其代碼的邏輯用中文解釋是這樣的
無論成功還是失敗都返回一個數(shù)組,數(shù)組的第一項是和錯誤相關(guān)的,數(shù)組的第二項是和響結(jié)果相關(guān)的
成功的話數(shù)組第一項也就是錯誤信息為空,數(shù)組第二項也就是響應(yīng)結(jié)果正常返回
失敗的話數(shù)組第一項也就是錯誤信息為錯誤信息,數(shù)組第二項也就是響應(yīng)結(jié)果返回undefined
經(jīng)過上面的分析我們可以認(rèn)定,世界上沒有什么黑魔法,沒有你做不到,只有你想不到。
這里我們再來看函數(shù)to的第二個參數(shù)errorExt不難發(fā)現(xiàn),這玩意其實就是拿來用戶自定義錯誤信息的,通過Object.assign將正常返回的error和用戶自定義和合并到一個對象里面供用戶自己選擇。
4結(jié)語
源碼不可怕,可怕的是自己的面對未知的恐懼感。
敢于面對,敢于嘗試,才能更上一層樓。
繼續(xù)加油,少年。
關(guān)注我,vx:codebangbang,掘金:愛嘿嘿的小黑。
5參考資料
倉庫地址:https://github.com/scopsy/await-to-js
官方文章:How to write async await without try-catch blocks in Javascript[2]
參考資料
[1]
https://segmentfault.com/a/1190000010244279: https://link.juejin.cn?target=https%3A%2F%2Fsegmentfault.com%2Fa%2F1190000010244279
[2]How to write async await without try-catch blocks in Javascript: https://blog.grossman.io/how-to-write-async-await-without-try-catch-blocks-in-javascript/
·················?若川簡介?·················
你好,我是若川,畢業(yè)于江西高校。現(xiàn)在是一名前端開發(fā)“工程師”。寫有《學(xué)習(xí)源碼整體架構(gòu)系列》20余篇,在知乎、掘金收獲超百萬閱讀。
從2014年起,每年都會寫一篇年度總結(jié),已經(jīng)寫了7篇,點擊查看年度總結(jié)。
同時,最近組織了源碼共讀活動,幫助3000+前端人學(xué)會看源碼。公眾號愿景:幫助5年內(nèi)前端人走向前列。
識別上方二維碼加我微信、拉你進(jìn)源碼共讀群
今日話題
略。分享、收藏、點贊、在看我的文章就是對我最大的支持~
總結(jié)
以上是生活随笔為你收集整理的如何优雅处理 async await 错误——解读小而美的 await-to-js 库的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 前端学习(3018):vue+eleme
- 下一篇: lisp6 暖通cad_这些高效插件,学