- 資訊首頁(yè) > 開(kāi)發(fā)技術(shù) >
- JavaScript中怎么實(shí)現錯誤處理
JavaScript中怎么實(shí)現錯誤處理,針對這個(gè)問(wèn)題,這篇文章詳細介紹了相對應的分析和解答,希望可以幫助更多想解決這個(gè)問(wèn)題的小伙伴找到更簡(jiǎn)單易行的方法。
使用 Promise 處理錯誤
為了演示 Promise 處理方式,我們先回到一開(kāi)始的那個(gè)事例:
function toUppercase(string) { if (typeof string !== "string") { throw TypeError("Wrong type given, expected a string"); } return string.toUpperCase(); } toUppercase(4);
相對簡(jiǎn)單拋出異常,我們可以使用 Promise.reject 和Promise.resolve:
function toUppercase(string) { if (typeof string !== "string") { return Promise.reject(TypeError("Wrong type given, expected a string")); } const result = string.toUpperCase(); return Promise.resolve(result); }
因為使用了 Promise ,所以可以使用 then 來(lái)接收返回的內容,或者用 catch 來(lái)捕獲出現的錯誤。
toUppercase(99) .then(result => result) .catch(error => console.error(error.message));
上面的執行結果:
Wrong type given, expected a string
除了 then 和 catch , Promise 中還有 finally 方法,這類(lèi)似于try/catch 中的 finally。
toUppercase(99) .then(result => result) .catch(error => console.error(error.message)) .finally(() => console.log("Run baby, run"));
Promise, error, 和 throw
使用 Promise.reject 可以很方便的拋出錯誤:
Promise.reject(TypeError("Wrong type given, expected a string"));
除了Promise.reject,我們也可以通過(guò)拋出異常來(lái)退出 Promise。
考慮以下示例:
Promise.resolve("A string").then(value => { if (typeof value === "string") { throw TypeError("Expected a number!"); } });
要停止異常傳播,我們照常使用catch:
Promise.resolve("A string") .then(value => { if (typeof value === "string") { throw TypeError("Expected a number!"); } }) .catch(reason => console.log(reason.message));
這種模式在fetch中很常見(jiàn):
fetch("https://example-dev/api/") .then(response => { if (!response.ok) { throw Error(response.statusText); } return response.json(); }) .then(json => console.log(json));
這里可以使用catch攔截異常。如果我們失敗了,或者決定不捕獲它,異??梢栽诙褩V凶杂擅芭?。
使用 Promise 來(lái)處理定時(shí)器中的異常
使用定時(shí)器或事件無(wú)法捕獲從回調引發(fā)的異常。
function failAfterOneSecond() { setTimeout(() => { throw Error("Something went wrong!"); }, 1000); } // DOES NOT WORK try { failAfterOneSecond(); } catch (error) { console.error(error.message); }
解決方案就是使用 Promise:
function failAfterOneSecond() { return new Promise((_, reject) => { setTimeout(() => { reject(Error("Something went wrong!")); }, 1000); }); }
使用reject,我們啟動(dòng)了一個(gè) Promise 拒絕,它攜帶一個(gè)錯誤對象。
此時(shí),我們可以使用catch處理異常:
failAfterOneSecond().catch(reason => console.error(reason.message));
使用 Promise.all 來(lái)處理錯誤
Promise.all(iterable) 方法返回一個(gè) Promise 實(shí)例,此實(shí)例在 iterable 參數內所有的 promise 都“完成(resolved)”或參數中不包含 promise 時(shí)回調完成(resolve);
const promise1 = Promise.resolve("All good!"); const promise2 = Promise.resolve("All good here too!"); Promise.all([promise1, promise2]).then((results) => console.log(results)); // [ 'All good!', 'All good here too!' ]
如果參數中 promise 有一個(gè)失敗(rejected),此實(shí)例回調失敗(reject),失敗的原因是第一個(gè)失敗 promise 的結果。
const promise1 = Promise.resolve("All good!"); const promise2 = Promise.reject(Error("No good, sorry!")); const promise3 = Promise.reject(Error("Bad day ...")); Promise.all([promise1, promise2, promise3]) .then(results => console.log(results)) .catch(error => console.error(error.message)); // No good, sorry!
同樣,無(wú)論Promise.all的結果如何運行函數,finally 都會(huì )被執行:
Promise.all([promise1, promise2, promise3]) .then(results => console.log(results)) .catch(error => console.error(error.message)) .finally(() => console.log("Always runs!"));
使用 Promise.any 來(lái)處理錯誤
Promise.any() (Firefox > 79, Chrome > 85) 接收一個(gè) Promise 可迭代對象,只要其中的一個(gè) promise 成功,就返回那個(gè)已經(jīng)成功的 promise 。如果可迭代對象中沒(méi)有一個(gè) promise 成功(即所有的 promises 都失敗/拒絕),就返回一個(gè)失敗的 promise 和AggregateError類(lèi)型的實(shí)例,它是 Error 的一個(gè)子類(lèi),用于把單一的錯誤集合在一起。本質(zhì)上,這個(gè)方法和Promise.all()是相反的。
const promise1 = Promise.reject(Error("No good, sorry!")); const promise2 = Promise.reject(Error("Bad day ...")); Promise.any([promise1, promise2]) .then(result => console.log(result)) .catch(error => console.error(error)) .finally(() => console.log("Always runs!"));
在這里,我們使用catch處理錯誤,輸出如下:
AggregateError: No Promise in Promise.any was resolved Always runs!
AggregateError對象具有與基本Error相同的屬性,外加errors屬性:
// .catch(error => console.error(error.errors)) //
此屬性是由reject產(chǎn)生的每個(gè)單獨錯誤的數組
[Error: "No good, sorry!, Error: "Bad day ..."]
使用 Promise.race 來(lái)處理錯誤
Promise.race(iterable) 方法返回一個(gè) promise,一旦迭代器中的某個(gè)promise解決或拒絕,返回的 promise就會(huì )解決或拒絕。
const promise1 = Promise.resolve("The first!"); const promise2 = Promise.resolve("The second!"); Promise.race([promise1, promise2]).then(result => console.log(result)); // The first!
這里說(shuō)明,第一個(gè) Promise 比第二個(gè)行執行完。那包含拒絕的情況又是怎么樣的?
const promise1 = Promise.resolve("The first!"); const rejection = Promise.reject(Error("Ouch!")); const promise2 = Promise.resolve("The second!"); Promise.race([promise1, rejection, promise2]).then(result => console.log(result) ); // The first!
如果把reject放在第一個(gè)又會(huì )怎么樣?
const promise1 = Promise.resolve("The first!"); const rejection = Promise.reject(Error("Ouch!")); const promise2 = Promise.resolve("The second!"); Promise.race([rejection, promise1, promise2]) .then(result => console.log(result)) .catch(error => console.error(error.message)); // Ouch!
使用 Promise.allSettled 來(lái)處理錯誤
Promise.allSettled()方法返回一個(gè)在所有給定的promise都已經(jīng)fulfilled或rejected后的promise,并帶有一個(gè)對象數組,每個(gè)對象表示對應的promise結果。
考慮下面示例:
const promise1 = Promise.resolve("Good!"); const promise2 = Promise.reject(Error("No good, sorry!")); Promise.allSettled([promise1, promise2]) .then(results => console.log(results)) .catch(error => console.error(error)) .finally(() => console.log("Always runs!"));
我們傳遞給Promise.allSettled一個(gè)由兩個(gè)Promise組成的數組:一個(gè)已解決,另一個(gè)被拒絕。
這種情況 catch 不會(huì )被執行, finally 永遠會(huì )執行。
[ { status: 'fulfilled', value: 'Good!' }, { status: 'rejected', reason: Error: No good, sorry! } ]
使用 async/await 來(lái)處理錯誤
為了簡(jiǎn)單起見(jiàn),我們使用前面的同步函數toUppercase,并通過(guò)在function關(guān)鍵字前放置async來(lái)將其轉換為異步函數
async function toUppercase(string) { if (typeof string !== "string") { throw TypeError("Wrong type given, expected a string"); } return string.toUpperCase(); }
只要在函數前面加上async,該函數就會(huì )返回一個(gè)Promise。這意味著(zhù)我們可以在函數調用之后進(jìn)行then、catch和finally 操作
async function toUppercase(string) { if (typeof string !== "string") { throw TypeError("Wrong type given, expected a string"); } return string.toUpperCase(); } toUppercase("abc") .then(result => console.log(result)) .catch(error => console.error(error.message)) .finally(() => console.log("Always runs!"));
當從 async 函數拋出異常時(shí),我們就可以使用 catch 來(lái)捕獲。
最重要的是,除了這種方式外,我們可以還使用try/catch/finally,就像我們使用同步函數所做的一樣。
async function toUppercase(string) { if (typeof string !== "string") { throw TypeError("Wrong type given, expected a string"); } return string.toUpperCase(); } async function consumer() { try { await toUppercase(98); } catch (error) { console.error(error.message); } finally { console.log("Always runs!"); } } consumer();
輸出:
Wrong type given, expected a string Always runs!
使用 async generators 來(lái)處理錯誤
JavaScript中的async generators是能夠生成 Promises 而不是簡(jiǎn)單值的生成器函數。
async function* asyncGenerator() { yield 33; yield 99; throw Error("Something went wrong!"); // Promise.reject }
基于 Promise,此處適用于錯誤處理的相同規則。在異步生成器中 throw 將會(huì )觸發(fā) Promise 的reject,我們可以使用catch對其進(jìn)行攔截。
為了使用異步生成器的 Promise,我們可以這樣做:
then 方法
異步遍歷
從上面我們知道,在兩次調用 yield之后,下一次會(huì )拋出一個(gè)異常:
const go = asyncGenerator(); go.next().then(value => console.log(value)); go.next().then(value => console.log(value)); go.next().catch(reason => console.error(reason.message));
輸出結果:
{ value: 33, done: false } { value: 99, done: false } Something went wrong!
別一種是使用 異步遍歷與for await...of:
async function* asyncGenerator() { yield 33; yield 99; throw Error("Something went wrong!"); // Promise.reject } async function consumer() { for await (const value of asyncGenerator()) { console.log(value); } } consumer();
有了 async/await 我們可以使用 try/catch 來(lái)捕獲異常:
async function* asyncGenerator() { yield 33; yield 99; throw Error("Something went wrong!"); // Promise.reject } async function consumer() { try { for await (const value of asyncGenerator()) { console.log(value); } } catch (error) { console.error(error.message); } } consumer();
輸出結果:
33 99 Something went wrong!
從異步生成器函數返回的迭代器對象也具有throw()方法,非常類(lèi)似于其同步副本。在此處的迭代器對象上調用throw()不會(huì )引發(fā)異常,但是會(huì )被Promise拒絕
async function* asyncGenerator() { yield 33; yield 99; yield 11; } const go = asyncGenerator(); go.next().then(value => console.log(value)); go.next().then(value => console.log(value)); go.throw(Error("Let's reject!")); go.next().then(value => console.log(value)); // value is undefined
要從外部處理這種情況,我們可以做:
go.throw(Error("Let's reject!")).catch(reason => console.error(reason.message));
Node 中的錯誤處理
Node 中的同步錯誤處理
Node.js 中的同步錯誤處理與到目前為止所看到的并沒(méi)有太大差異。對于同步,使用 try/catch/finally 就可以很好的工作了。
Node.js 中的異步錯誤處理:回調模式
對于異步代碼,Node.js 主要使用這兩種方式:
回調模式
event emitters
在回調模式中,異步 Node.js API 接受一個(gè)函數,該函數通過(guò)事件循環(huán)處理,并在調用堆棧為空時(shí)立即執行。
考慮以下代碼:
const { readFile } = require("fs"); function readDataset(path) { readFile(path, { encoding: "utf8" }, function(error, data) { if (error) console.error(error); // do stuff with the data }); }
我們可以看到,這里處理錯誤的方式是使用了回調:
// function(error, data) { if (error) console.error(error); // do stuff with the data } //
如果使用fs.readFile讀取給定路徑而引起任何錯誤,我們將獲得一個(gè)錯誤對象。
在這一點(diǎn)上,我們可以:
簡(jiǎn)單的把對象錯誤打出來(lái)
拋出錯誤
把錯誤傳到另一個(gè)回調
我們可以?huà)伋鲆粋€(gè)異常
const { readFile } = require("fs"); function readDataset(path) { readFile(path, { encoding: "utf8" }, function(error, data) { if (error) throw Error(error.message); // do stuff with the data }); }
但是,與 DOM 中的事件和定時(shí)器一樣,此異常將使程序崩潰。通過(guò)try/catch捕獲它是不起作用的:
const { readFile } = require("fs"); function readDataset(path) { readFile(path, { encoding: "utf8" }, function(error, data) { if (error) throw Error(error.message); // do stuff with the data }); } try { readDataset("not-here.txt"); } catch (error) { console.error(error.message); }
如果我們不想使程序崩潰,則將錯誤傳遞給另一個(gè)回調是首選方法:
const { readFile } = require("fs"); function readDataset(path) { readFile(path, { encoding: "utf8" }, function(error, data) { if (error) return errorHandler(error); // do stuff with the data }); }
這里的errorHandler顧名思義,是一個(gè)用于錯誤處理的簡(jiǎn)單函數:
function errorHandler(error) { console.error(error.message); // do something with the error: // - write to a log. // - send to an external logger. }
Node.js 中的異步錯誤處理:event emitters
在 Node.js 中所做的大部分工作都是基于事件的。大多數情況下,emitter object 和一些觀(guān)察者進(jìn)行交互以偵聽(tīng)消息。
Node.js中的任何事件驅動(dòng)模塊(例如net)都擴展了一個(gè)名為EventEmitter的根類(lèi)。
Node.js中的EventEmitter有兩種基本方法:on和emit。
考慮以下簡(jiǎn)單的 HTTP :
const net = require("net"); const server = net.createServer().listen(8081, "127.0.0.1"); server.on("listening", function () { console.log("Server listening!"); }); server.on("connection", function (socket) { console.log("Client connected!"); socket.end("Hello client!"); });
這里我們來(lái)聽(tīng)兩個(gè)事件:listening 和connection。除了這些事件之外,event emitters 還公開(kāi)一個(gè) error 事件,以防發(fā)生錯誤。
如果在端口80上運行這段代碼,而不是在前面的示例上偵聽(tīng),將會(huì )得到一個(gè)異常:
const net = require("net"); const server = net.createServer().listen(80, "127.0.0.1"); server.on("listening", function () { console.log("Server listening!"); }); server.on("connection", function (socket) { console.log("Client connected!"); socket.end("Hello client!"); });
輸出:
events.js:291 throw er; // Unhandled 'error' event ^ Error: listen EACCES: permission denied 127.0.0.1:80 Emitted 'error' event on Server instance at: ...
要捕獲它,我們可以注冊一個(gè)error事件處理程序:
server.on("error", function(error) { console.error(error.message); });
輸出結果:
listen EACCES: permission denied 127.0.0.1:80
免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng )、來(lái)自互聯(lián)網(wǎng)轉載和分享為主,文章觀(guān)點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權請聯(lián)系QQ:712375056 進(jìn)行舉報,并提供相關(guān)證據,一經(jīng)查實(shí),將立刻刪除涉嫌侵權內容。
Copyright ? 2009-2021 56dr.com. All Rights Reserved. 特網(wǎng)科技 特網(wǎng)云 版權所有 珠海市特網(wǎng)科技有限公司 粵ICP備16109289號
域名注冊服務(wù)機構:阿里云計算有限公司(萬(wàn)網(wǎng)) 域名服務(wù)機構:煙臺帝思普網(wǎng)絡(luò )科技有限公司(DNSPod) CDN服務(wù):阿里云計算有限公司 中國互聯(lián)網(wǎng)舉報中心 增值電信業(yè)務(wù)經(jīng)營(yíng)許可證B2
建議您使用Chrome、Firefox、Edge、IE10及以上版本和360等主流瀏覽器瀏覽本網(wǎng)站