摘要:在我的上一篇文章中寫到,當(dāng)使用時,如何同時捕獲到回調(diào)函數(shù)和拋出的錯誤。而對于操作則會返回一個,我們能夠輕松地通過捕獲到異常不管是回調(diào)函數(shù)還是,他們都是異步的,我們的應(yīng)用程序都不會因為發(fā)送而被阻塞。
原文鏈接:Catching without Awaiting
當(dāng)執(zhí)行一項需要等待一段時間才能返回的任務(wù)時,如果使用async/await,就顯得比較麻煩了。如果async方法還沒有得到返回值,我們就捕獲不到其中的異常。
在我的上一篇文章Learn to Throw Again中寫到,當(dāng)使用async/await時,如何同時捕獲到回調(diào)函數(shù)和throw拋出的錯誤。在這篇文章中,我們將討論如何在“后臺”中執(zhí)行異步操作并捕獲異常(這里使用雙引號,因為在單線程平臺上沒有真正的后臺操作)
從回調(diào)函數(shù)的模式開始,思考下列代碼:
function email(user, message, callback) { if (!user) { // 拋出異常 throw new Error("Invlid user"); } if (!user.address) { // 回調(diào)函數(shù),可能拋出異常 return callback(); } // 異步的 return mailer.send(user.address, message, callback); }
上述代碼遵循典型的throw-on-bad-input / callback-asynchronous-errors模式(一旦程序接收到錯誤的輸入,異步拋出異常),如果我們想要發(fā)出一封郵件,我們這樣調(diào)用:
email(user, message, () => {});
對于非法的輸入,調(diào)用這個函數(shù)依舊可能拋出異常。但是,如果電子郵件在傳輸中產(chǎn)生錯誤,這個函數(shù)調(diào)用時會忽略異步拋出的錯誤。
我們把它改為Promise的版本:
function email(user, message) { if (!user) { throw new Error("Invlid user"); } if (!user.address) { return Promise.resolve(); } return mailer.send(user.address, message); // 函數(shù)返回一個Promise }
這樣,對于非法的輸入,依舊可以捕獲到異常。而對于mailer.send()操作則會返回一個Promise,我們能夠輕松地通過Promise.catch()捕獲到異常:
email(user, message).catch(() => {});
不管是回調(diào)函數(shù)還是Promise,他們都是異步的,我們的應(yīng)用程序都不會因為email發(fā)送而被阻塞。
對于async/await的模式,如果在try...catch語句中不使用await關(guān)鍵字,那么try...catch子句不會真正工作。來看下面的async版本:
function email(user, message) { if (!user) { throw new Error("Invlid user"); } if (!user.address) { return; } return mailer.send(user.address, message); // async function }
如果我們像這樣去調(diào)用:
try { email(user, message); } catch (err) { Bounce.rethrow(err, "system"); }
對于非法的輸入錯誤,仍然會正常地拋出異常,這沒問題。但是對于任何異步返回的異常,例如在mailer.send()拋出的異常,則會被忽略掉。不管這種錯誤我們想不想捕獲到,反正都是捕獲不到的。為了修補這個bug,則要使用await關(guān)鍵字。但是問題來了,這將會導(dǎo)致整個“后臺操作”的阻塞。
有一種方案是混用async/await和Promise:
email(user, message).catch(() => {});
但這樣的問題在于,對于沒有address的用戶,這個方法返回的返回值類型并不是Promise,因而其也不會有catch()方法,因此程序會出現(xiàn)TypeError: Cannot read property ‘catch’ of undefined這樣的錯誤。
你可能會嘗試直接把email()函數(shù)聲明為async函數(shù), 并使得它一定會返回一個Promise,但是這并不是一個很好的解決方案,因為async / await其實也只是Promise對象的一層包裝。如果不使用await關(guān)鍵字,把一個函數(shù)聲明為async函數(shù)是完全沒有必要的。因為async函數(shù)總是要通過返回一個Promise,通過next-tick拿到結(jié)果,這樣會浪費Promise包裝和next-tick事件循環(huán)機制所造成的性能損耗。
此外,如果要在循環(huán)中使用async函數(shù),并且這個循環(huán)中執(zhí)行了很多任務(wù),但是其實很多任務(wù)并不是真正意義上異步的,那就沒有必要使用async / await,可以參考hapi.js中的checking if you really need to await下列代碼判斷是否真的需要使用await,這樣或許能獲得一些性能的提升:
var response = (typeof func === "function" ? func(this) : this._invoke(func)); if (response && typeof response.then === "function") { // Skip await if no reason to response = await response; }
判斷是否真的需要await,其實就是判斷其是否存在then方法,并且then方法是一個函數(shù)。因為await的作用其實就是取得一個異步操作的返回結(jié)果。
如果你能夠保證email方法總是返回一個Promise,我們可以通過更改我們的email()函數(shù)來達(dá)到這一點,但這樣就顯得急功近利了!代碼顯得十分不簡潔,而且使用了很不必要的異步操作。在一個完整的async/await函數(shù)調(diào)用棧中,不需要我們手動構(gòu)建Promise。對于這個例子來說還好,更重要的是,我們不可能總通過改變email()方法來實現(xiàn),因為這只是一個例子,在實際運用中,可能email()方法是通過模塊引入的。
其中一種解決方案是通過await關(guān)鍵字來調(diào)用async函數(shù)。通常情況下,在一個函數(shù)中使用阻塞操作,如果不等待這個函數(shù)執(zhí)行完成,它不會拋出異常,但是我們可以通過try...catch來包裹:
async function backgroundEmail(user, message) { try { await email(user, message); } catch (err) { Bounce.rethrow(err, "system"); } }
然后不通過await調(diào)用backgroundEmail:
backgroundEmail(user, message);
這樣我們不但能夠捕獲到應(yīng)用程序的異常,還能夠捕獲到異步拋出的異常。
為了讓異常捕獲更加簡單,我們使用Bounce模塊,它提供了一個background()方法。
Bounce.background(() => email(user, message));
如果我們使用Node.js的AssertionError原型,這樣就能夠使得Bounce拋出輸入異常的錯誤了。
async/await函數(shù)去除了一些同步函數(shù)(() => {})的功能,為了達(dá)到和普通函數(shù)相同的效果,我們不得不寫一些額外的代碼來實現(xiàn)。但是使用新的工具庫,可以很簡便地突破這一限制。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/90782.html
摘要:異步函數(shù)是值通過事件循環(huán)異步執(zhí)行的函數(shù),它會通過一個隱式的返回其結(jié)果。 async 異步函數(shù) 不完全使用攻略 前言 現(xiàn)在已經(jīng)到 8012 年的尾聲了,前端各方面的技術(shù)發(fā)展也層出不窮,VueConf TO 2018 大會 也發(fā)布了 Vue 3.0的計劃。而在我們(我)的日常中也經(jīng)常用 Vue 來編寫一些項目。那么,就少不了 ES6 的登場了。那么話說回來,你真的會用 ES6 的 asyn...
摘要:等待的基本語法該關(guān)鍵字的的意思就是讓編譯器等待并返回結(jié)果。這里并不會占用資源,因為引擎可以同時執(zhí)行其他任務(wù)其他腳本或處理事件。接下來,我們寫一個火箭發(fā)射場景的小例子不是真的發(fā)射火箭 本文由云+社區(qū)發(fā)表 本篇文章,小編將和大家一起學(xué)習(xí)異步編程的未來——async/await,它會打破你對上篇文章Promise的認(rèn)知,竟然異步代碼還能這么寫! 但是別太得意,你需要深入理解Promise后,...
摘要:取而代之,利用事件循環(huán)體系,使用了一種類似語法的工作方式一旦非阻塞的異步操作完成之后,就可以讓開發(fā)者分配的回調(diào)函數(shù)被觸發(fā)。第一個嘗試嵌套的回調(diào)函數(shù)下面是使用嵌套的回調(diào)函數(shù)的實現(xiàn)方法這可能對于任何使用者來說再熟悉不過了。 寫在文章前 這篇文章翻譯自 ASYNC/AWAIT WILL MAKE YOUR CODE SIMPLER,這是一篇寫于2017年八月的文章,并由某專欄提名為17年十大...
摘要:能夠捕獲非異步的異常。來匹配正常異常的情況。在中處理所有的異常如果出錯,則退出。所以,的模式使得異常處理變得非常簡潔。自從年雙十一正式上線,累計處理了億錯誤事件,付費客戶有陽光保險核桃編程荔枝掌門對微脈青團(tuán)社等眾多品牌企業(yè)。 譯者按: 使用.catch()來捕獲所有的異常 原文: Async Await Error Handling in JavaScript 譯者: Fundeb...
摘要:函數(shù)會在之后的某個時刻觸發(fā)事件定時器。事件循環(huán)中的這樣一次遍歷被稱為一個。執(zhí)行完畢并出棧。當(dāng)定時器過期,宿主環(huán)境會把回調(diào)函數(shù)添加至事件循環(huán)隊列中,然后,在未來的某個取出并執(zhí)行該事件。 原文請查閱這里,略有改動。 本系列持續(xù)更新中,Github 地址請查閱這里。 這是 JavaScript 工作原理的第四章。 現(xiàn)在,我們將會通過回顧單線程環(huán)境下編程的弊端及如何克服這些困難以創(chuàng)建令人驚嘆...
閱讀 805·2021-09-22 16:01
閱讀 2098·2021-08-20 09:37
閱讀 1702·2019-08-30 15:54
閱讀 1700·2019-08-30 15:44
閱讀 846·2019-08-28 18:23
閱讀 3024·2019-08-26 12:17
閱讀 1026·2019-08-26 11:56
閱讀 1548·2019-08-23 16:20