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