摘要:總結(jié)最后總結(jié)一下從異步函數(shù)拋出的錯(cuò)誤不會(huì)是普通的異常。異步函數(shù)和異步方法總是返回一個(gè),無(wú)論是已解決還是被拒絕。要攔截異步函數(shù)中的異常,必須使用。
翻譯:瘋狂的技術(shù)宅
原文:https://www.valentinog.com/bl...
本文首發(fā)微信公眾號(hào):jingchengyideng
歡迎關(guān)注,每天都給你推送新鮮的前端技術(shù)文章
可以在 Javascript 的異步函數(shù)中拋出錯(cuò)誤嗎?
這個(gè)話題已被反復(fù)提起過(guò)幾百次,不過(guò)這次讓我們從TDD(Test-Driven Development)的角度來(lái)回答它。
如果你能不在Stackoverflow上搜索就能回答這個(gè)問(wèn)題,會(huì)給我留下深刻的印象。
如果不能的話也可以很酷。 繼續(xù)往下讀,你就能學(xué)到!
你將學(xué)到什么通過(guò)后面的內(nèi)容你將學(xué)到:
如何從 Javascript 的異步函數(shù)中拋出錯(cuò)誤
如何使用 Jest 測(cè)試來(lái)自異步函數(shù)的異常
要求要繼續(xù)往下讀你應(yīng)該:
對(duì) Javascript 和 ES6 有基本的了解
安裝 Node.Js 和 Jest
如何從 Javascript 的常規(guī)函數(shù)中拋出錯(cuò)誤使用異常而不是返回碼(清潔代碼)。
拋出錯(cuò)誤是處理未知的最佳方法。
同樣的規(guī)則適用于各種現(xiàn)代語(yǔ)言:Java、Javascript、Python、Ruby。
你可以從函數(shù)中拋出錯(cuò)誤,可以參照以下示例:
function upperCase(name) { if (typeof name !== "string") { throw TypeError("name must be a string"); } return name.toUpperCase(); } module.exports = upperCase;
這是對(duì)它的測(cè)試(使用Jest):
"use strict"; const assert = require("assert"); const upperCase = require("../function"); describe("upperCase function", () => { test("it throws when name is not provided", () => { assert.throws(() => upperCase()); }); test("it throws when name is not a string", () => { assert.throws(() => upperCase(9)); }); });
也可以從 ES6 的類(lèi)中拋出錯(cuò)誤。在 Javascript 中編寫(xiě)類(lèi)時(shí),我總是在構(gòu)造函數(shù)中輸入意外值。下面是一個(gè)例子:
class Person { constructor(name) { if (typeof name !== "string") { throw TypeError("name must be a string"); } this.name = name; } // some method here } module.exports = Person;
以下是該類(lèi)的測(cè)試:
"use strict"; const assert = require("assert"); const Person = require("../index"); describe("Person class", () => { test("it throws when name is not provided", () => { assert.throws(() => new Person()); }); test("it throws when name is not a string", () => { assert.throws(() => new Person(9)); }); });
測(cè)試確實(shí)通過(guò)了:
PASS test/index.test.js Person class ? it throws when name is not provided (1ms) ? it throws when name is not a string
安排的明明白白!
所以無(wú)論異常是從常規(guī)函數(shù)還是從類(lèi)構(gòu)造函數(shù)(或從方法)拋出的,一切都會(huì)按照預(yù)期工作。
但是如果我想從異步函數(shù)中拋出錯(cuò)誤怎么辦?
我可以在測(cè)試中使用assert.throws嗎?
各位看官請(qǐng)上眼!
測(cè)試異常既然都看到這里了,所以你應(yīng)該知道什么是 Javascript 的異步函數(shù),對(duì)嗎?先看一段代碼:
class Person { constructor(name) { if (typeof name !== "string") { throw TypeError("name must be a string"); } this.name = name; } // some method here } module.exports = Person;
假設(shè)你要添加異步方法來(lái)獲取有關(guān)該人的數(shù)據(jù)。這種方法需要一個(gè)網(wǎng)址。如果url不是字符串,就要像上一個(gè)例子中那樣拋出錯(cuò)誤。
先來(lái)修改一下這個(gè)類(lèi):
class Person { constructor(name) { if (typeof name !== "string") { throw TypeError("name must be a string"); } this.name = name; } async getData(url) { if (typeof url !== "string") { throw TypeError("url must be a string"); } // const response = await fetch(url) // do stuff } } module.exports = Person;
如果我運(yùn)行代碼會(huì)怎么樣?試試吧:
const Person = require("../index"); const valentinogagliardi = new Person("valentinogagliardi"); valentinogagliardi.getData();
結(jié)果是這樣
UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): TypeError: name must be a string DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
果然不出所料,異步方法返回了一個(gè)Promise rejection,從嚴(yán)格意義上來(lái)講,并沒(méi)有拋出什么東西。錯(cuò)誤被包含在了Promise rejection中。
換句話說(shuō),我不能使用 assert.throws 來(lái)測(cè)試它。
讓我們通過(guò)測(cè)試來(lái)驗(yàn)證一下:
"use strict"; const assert = require("assert"); const Person = require("../index"); describe("Person methods", () => { test("it throws when url is not a string", () => { const valentinogagliardi = new Person("valentinogagliardi"); assert.throws(() => valentinogagliardi.getData()); }); });
測(cè)試失敗了!
FAIL test/index.test.js Person methods ? it throws when url is not a string assert.throws(function) Expected the function to throw an error. But it didn"t throw anything. Message: Missing expected exception.
有沒(méi)有悟出點(diǎn)什么?
看把你能的,來(lái)抓我啊從嚴(yán)格意義上講異步函數(shù)和異步方法不會(huì)拋出錯(cuò)誤。異步函數(shù)和異步方法總是返回一個(gè)Promise,無(wú)論它已完成還是被拒絕,你必須附上 then() 和 catch(),無(wú)論如何。(或者將方法包裝在try/catch中)。被拒絕的Promise將會(huì)在堆棧中傳播,除非你抓?。╟atch)它。
至于測(cè)試代碼,應(yīng)該這樣寫(xiě):
"use strict"; const assert = require("assert"); const Person = require("../index"); describe("Person methods", () => { test("it rejects when url is not a string", async () => { expect.assertions(1); const valentinogagliardi = new Person("valentinogagliardi"); await expect(valentinogagliardi.getData()).rejects.toEqual( TypeError("url must be a string") ); }); });
我們測(cè)試的不能是普通的異常,而是帶有TypeError的rejects。
現(xiàn)在測(cè)試通過(guò)了:
PASS test/index.test.js Person methods ? it rejects when url is not a string
那代碼該怎么寫(xiě)呢?為了能夠捕獲錯(cuò)誤,你應(yīng)該這樣重構(gòu):
const Person = require("../index"); const valentinogagliardi = new Person("valentinogagliardi"); valentinogagliardi .getData() .then(res => res) .catch(err => console.error(err));
現(xiàn)在異常將會(huì)出現(xiàn)在控制臺(tái)中:
TypeError: url must be a string at Person.getData (/home/valentino/Documenti/articles-and-broadcasts/throw-from-async-functions-2018-04-02/index.js:12:13) at Object.(/home/valentino/Documenti/articles-and-broadcasts/throw-from-async-functions-2018-04-02/index.js:22:4) // ...
如果你想要更多的try/catch.,有一件重要的事需要注意。
下面的代碼不會(huì)捕獲錯(cuò)誤:
const Person = require("../index"); async function whatever() { try { const valentinogagliardi = new Person("valentinogagliardi"); await valentinogagliardi.getData(); // do stuff with the eventual result and return something } catch (error) { throw Error(error); } } whatever();
記?。罕痪芙^的Promise會(huì)在堆棧中傳播,除非你抓住(catch)它。
要在 try/catch 中正確捕獲錯(cuò)誤,可以像這樣重構(gòu):
async function whatever() { try { const valentinogagliardi = new Person("valentinogagliardi"); await valentinogagliardi.getData(); // do stuff with the eventual result and return something } catch (error) { throw Error(error); } } whatever().catch(err => console.error(err));
這就是它的工作原理。
總結(jié)最后總結(jié)一下:
從異步函數(shù)拋出的錯(cuò)誤不會(huì)是“普通的異常”。
異步函數(shù)和異步方法總是返回一個(gè)Promise,無(wú)論是已解決還是被拒絕。
要攔截異步函數(shù)中的異常,必須使用catch()。
以下是在Jest中測(cè)試異常的規(guī)則:
使用 assert.throws 來(lái)測(cè)試普通函數(shù)和方法中的異常
使用 expect + rejects 來(lái)測(cè)試異步函數(shù)和異步方法中的異常
如果你對(duì)如何使用 Jest 測(cè)試 Koa 2 感興趣,請(qǐng)查看使用Jest和Supertest進(jìn)行測(cè)試的簡(jiǎn)紹這篇文章。
感謝閱讀!
歡迎掃描二維碼關(guān)注公眾號(hào),每天都給你推送新鮮的前端技術(shù)文章
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/8906.html
摘要:總結(jié)最后總結(jié)一下從異步函數(shù)拋出的錯(cuò)誤不會(huì)是普通的異常。異步函數(shù)和異步方法總是返回一個(gè),無(wú)論是已解決還是被拒絕。要攔截異步函數(shù)中的異常,必須使用。 翻譯:瘋狂的技術(shù)宅原文:https://www.valentinog.com/bl... 本文首發(fā)微信公眾號(hào):jingchengyideng歡迎關(guān)注,每天都給你推送新鮮的前端技術(shù)文章 可以在 Javascript 的異步函數(shù)中拋出錯(cuò)誤嗎...
摘要:?jiǎn)卧獪y(cè)試會(huì)體現(xiàn)出以上錯(cuò)誤處理程序的作用如果出現(xiàn)問(wèn)題,錯(cuò)誤處理程序就會(huì)返回。同時(shí)錯(cuò)誤會(huì)展開(kāi)堆棧,這對(duì)調(diào)試非常有幫助。展開(kāi)堆棧處理異常的一種方式是在調(diào)用堆棧的頂部加入。確保你的錯(cuò)誤處理處在相同域中,這樣會(huì)保留原始消息,堆棧和自定義錯(cuò)誤對(duì)象。 JavaScript的事件驅(qū)動(dòng)范式增添了豐富的語(yǔ)言,也是讓使用JavaScript編程變得更加多樣化。如果將瀏覽器設(shè)想為JavaScript的事件驅(qū)動(dòng)...
摘要:函數(shù)會(huì)在之后的某個(gè)時(shí)刻觸發(fā)事件定時(shí)器。事件循環(huán)中的這樣一次遍歷被稱(chēng)為一個(gè)。執(zhí)行完畢并出棧。當(dāng)定時(shí)器過(guò)期,宿主環(huán)境會(huì)把回調(diào)函數(shù)添加至事件循環(huán)隊(duì)列中,然后,在未來(lái)的某個(gè)取出并執(zhí)行該事件。 原文請(qǐng)查閱這里,略有改動(dòng)。 本系列持續(xù)更新中,Github 地址請(qǐng)查閱這里。 這是 JavaScript 工作原理的第四章。 現(xiàn)在,我們將會(huì)通過(guò)回顧單線程環(huán)境下編程的弊端及如何克服這些困難以創(chuàng)建令人驚嘆...
摘要:因?yàn)槁酚蓪用媸軜I(yè)務(wù)影響很大,經(jīng)常修改一些功能的行為,所以后來(lái)大部分測(cè)試都是針對(duì)層面的單元測(cè)試。在我了解的過(guò)程中,我發(fā)現(xiàn)中文網(wǎng)絡(luò)上對(duì)的討論非常分散,于是我創(chuàng)建了中文社區(qū),到年末已經(jīng)有個(gè)注冊(cè)用戶(hù)和個(gè)帖子了。 https://jysperm.me/2016/02/programming-of-2015/ 從 2014 年末開(kāi)始開(kāi)發(fā)的一個(gè)互聯(lián)網(wǎng)金融項(xiàng)目終于在今年三月份上線了,這是一個(gè) Node...
閱讀 2421·2021-11-18 10:02
閱讀 1935·2021-10-13 09:40
閱讀 3013·2021-09-07 10:07
閱讀 2120·2021-09-04 16:48
閱讀 1017·2019-08-30 13:18
閱讀 2463·2019-08-29 14:03
閱讀 2931·2019-08-29 12:54
閱讀 3169·2019-08-26 11:41