自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

關(guān)于 JavaScript 錯誤處理的最完整指南(下半部)

開發(fā) 前端
在這個指南中,我們介紹了JavaScript的各種錯誤處理,從簡單的同步代碼到高級的異步。在JavaScript程序中,可以通過多種方式來捕獲異常。

[[342070]]

使用 Promise 處理錯誤

為了演示 Promise 處理方式,我們先回到一開始的那個事例:

  1. function toUppercase(string) { 
  2.   if (typeof string !== "string") { 
  3.     throw TypeError("Wrong type given, expected a string"); 
  4.   } 
  5.  
  6.   return string.toUpperCase(); 
  7.  
  8. toUppercase(4); 

相對簡單拋出異常,我們可以使用 Promise.reject 和Promise.resolve:

  1. function toUppercase(string) { 
  2.   if (typeof string !== "string") { 
  3.     return Promise.reject(TypeError("Wrong type given, expected a string")); 
  4.   } 
  5.  
  6.   const result = string.toUpperCase(); 
  7.  
  8.   return Promise.resolve(result); 

因為使用了 Promise ,所以可以使用 then 來接收返回的內(nèi)容,或者用 catch 來捕獲出現(xiàn)的錯誤。

  1. toUppercase(99) 
  2.   .then(result => result) 
  3.   .catch(error => console.error(error.message)); 

上面的執(zhí)行結(jié)果:

  1. Wrong type given, expected a string 

除了 then 和 catch , Promise 中還有 finally 方法,這類似于try/catch 中的 finally。

  1. toUppercase(99) 
  2.   .then(result => result) 
  3.   .catch(error => console.error(error.message)) 
  4.   .finally(() => console.log("Run baby, run")); 

Promise, error, 和 throw

使用 Promise.reject 可以很方便的拋出錯誤:

  1. Promise.reject(TypeError("Wrong type given, expected a string")); 

除了Promise.reject,我們也可以通過拋出異常來退出 Promise。

考慮以下示例:

  1. Promise.resolve("A string").then(value => { 
  2.   if (typeof value === "string") { 
  3.     throw TypeError("Expected a number!"); 
  4.   } 
  5. }); 

要停止異常傳播,我們照常使用catch:

  1. Promise.resolve("A string"
  2.   .then(value => { 
  3.     if (typeof value === "string") { 
  4.       throw TypeError("Expected a number!"); 
  5.     } 
  6.   }) 
  7.   .catch(reason => console.log(reason.message)); 

這種模式在fetch中很常見:

  1. fetch("https://example-dev/api/"
  2.   .then(response => { 
  3.     if (!response.ok) { 
  4.       throw Error(response.statusText); 
  5.     } 
  6.  
  7.     return response.json(); 
  8.   }) 
  9.   .then(json => console.log(json)); 

這里可以使用catch攔截異常。如果我們失敗了,或者決定不捕獲它,異??梢栽诙褩V凶杂擅芭?。

使用 Promise 來處理定時器中的異常

使用定時器或事件無法捕獲從回調(diào)引發(fā)的異常。

  1. function failAfterOneSecond() { 
  2.   setTimeout(() => { 
  3.     throw Error("Something went wrong!"); 
  4.   }, 1000); 
  5.  
  6. // DOES NOT WORK 
  7. try { 
  8.   failAfterOneSecond(); 
  9. } catch (error) { 
  10.   console.error(error.message); 

解決方案就是使用 Promise:

  1. function failAfterOneSecond() { 
  2.   return new Promise((_, reject) => { 
  3.     setTimeout(() => { 
  4.       reject(Error("Something went wrong!")); 
  5.     }, 1000); 
  6.   }); 

使用reject,我們啟動了一個 Promise 拒絕,它攜帶一個錯誤對象。

此時,我們可以使用catch處理異常:

  1. failAfterOneSecond().catch(reason => console.error(reason.message)); 

使用 Promise.all 來處理錯誤

Promise.all(iterable) 方法返回一個 Promise 實例,此實例在 iterable 參數(shù)內(nèi)所有的 promise 都“完成(resolved)”或參數(shù)中不包含 promise 時回調(diào)完成(resolve);

  1. const promise1 = Promise.resolve("All good!"); 
  2. const promise2 = Promise.resolve("All good here too!"); 
  3.  
  4. Promise.all([promise1, promise2]).then((results) => console.log(results)); 
  5.  
  6. // [ 'All good!''All good here too!' ] 

如果參數(shù)中 promise 有一個失敗(rejected),此實例回調(diào)失敗(reject),失敗的原因是第一個失敗 promise 的結(jié)果。

  1. const promise1 = Promise.resolve("All good!"); 
  2. const promise2 = Promise.reject(Error("No good, sorry!")); 
  3. const promise3 = Promise.reject(Error("Bad day ...")); 
  4.  
  5. Promise.all([promise1, promise2, promise3]) 
  6.   .then(results => console.log(results)) 
  7.   .catch(error => console.error(error.message)); 
  8.  
  9. // No good, sorry! 

同樣,無論Promise.all的結(jié)果如何運行函數(shù),finally 都會被執(zhí)行:

  1. Promise.all([promise1, promise2, promise3]) 
  2.   .then(results => console.log(results)) 
  3.   .catch(error => console.error(error.message)) 
  4.   .finally(() => console.log("Always runs!")); 

使用 Promise.any 來處理錯誤

Promise.any() (Firefox > 79, Chrome > 85) 接收一個 Promise 可迭代對象,只要其中的一個 promise 成功,就返回那個已經(jīng)成功的 promise 。如果可迭代對象中沒有一個 promise 成功(即所有的 promises 都失敗/拒絕),就返回一個失敗的 promise 和AggregateError類型的實例,它是 Error 的一個子類,用于把單一的錯誤集合在一起。本質(zhì)上,這個方法和Promise.all()是相反的。

  1. const promise1 = Promise.reject(Error("No good, sorry!")); 
  2. const promise2 = Promise.reject(Error("Bad day ...")); 
  3.  
  4. Promise.any([promise1, promise2]) 
  5.   .then(result => console.log(result)) 
  6.   .catch(error => console.error(error)) 
  7.   .finally(() => console.log("Always runs!")); 

在這里,我們使用catch處理錯誤,輸出如下:

  1. AggregateError: No Promise in Promise.any was resolved 
  2. Always runs! 

AggregateError對象具有與基本Error相同的屬性,外加errors屬性:

  1. // 
  2.   .catch(error => console.error(error.errors)) 
  3. // 

此屬性是由reject產(chǎn)生的每個單獨錯誤的數(shù)組

  1. [Error: "No good, sorry!, Error: "Bad day ..."] 

使用 Promise.race 來處理錯誤

Promise.race(iterable) 方法返回一個 promise,一旦迭代器中的某個promise解決或拒絕,返回的 promise就會解決或拒絕。

  1. const promise1 = Promise.resolve("The first!"); 
  2. const promise2 = Promise.resolve("The second!"); 
  3.  
  4. Promise.race([promise1, promise2]).then(result => console.log(result)); 
  5.  
  6. // The first

這里說明,第一個 Promise 比第二個行執(zhí)行完。那包含拒絕的情況又是怎么樣的?

  1. const promise1 = Promise.resolve("The first!"); 
  2. const rejection = Promise.reject(Error("Ouch!")); 
  3. const promise2 = Promise.resolve("The second!"); 
  4.  
  5. Promise.race([promise1, rejection, promise2]).then(result => 
  6.   console.log(result) 
  7. ); 
  8.  
  9. // The first

如果把reject放在第一個又會怎么樣?

  1. const promise1 = Promise.resolve("The first!"); 
  2. const rejection = Promise.reject(Error("Ouch!")); 
  3. const promise2 = Promise.resolve("The second!"); 
  4.  
  5. Promise.race([rejection, promise1, promise2]) 
  6.   .then(result => console.log(result)) 
  7.   .catch(error => console.error(error.message)); 
  8.  
  9. // Ouch! 

使用 Promise.allSettled 來處理錯誤

Promise.allSettled()方法返回一個在所有給定的promise都已經(jīng)fulfilled或rejected后的promise,并帶有一個對象數(shù)組,每個對象表示對應(yīng)的promise結(jié)果。

考慮下面示例:

  1. const promise1 = Promise.resolve("Good!"); 
  2. const promise2 = Promise.reject(Error("No good, sorry!")); 
  3.  
  4. Promise.allSettled([promise1, promise2]) 
  5.   .then(results => console.log(results)) 
  6.   .catch(error => console.error(error)) 
  7.   .finally(() => console.log("Always runs!")); 

我們傳遞給Promise.allSettled一個由兩個Promise組成的數(shù)組:一個已解決,另一個被拒絕。

這種情況 catch 不會被執(zhí)行, finally 永遠會執(zhí)行。

  1.   { status: 'fulfilled', value: 'Good!' }, 
  2.   { 
  3.     status: 'rejected'
  4.     reason: Error: No good, sorry! 
  5.   } 

使用 async/await 來處理錯誤

為了簡單起見,我們使用前面的同步函數(shù)toUppercase,并通過在function關(guān)鍵字前放置async來將其轉(zhuǎn)換為異步函數(shù)

  1. async function toUppercase(string) { 
  2.   if (typeof string !== "string") { 
  3.     throw TypeError("Wrong type given, expected a string"); 
  4.   } 
  5.  
  6.   return string.toUpperCase(); 

只要在函數(shù)前面加上async,該函數(shù)就會返回一個Promise。這意味著我們可以在函數(shù)調(diào)用之后進行then、catch和finally 操作

  1. async function toUppercase(string) { 
  2.   if (typeof string !== "string") { 
  3.     throw TypeError("Wrong type given, expected a string"); 
  4.   } 
  5.  
  6.   return string.toUpperCase(); 
  7.  
  8. toUppercase("abc"
  9.   .then(result => console.log(result)) 
  10.   .catch(error => console.error(error.message)) 
  11.   .finally(() => console.log("Always runs!")); 

當從 async 函數(shù)拋出異常時,我們就可以使用 catch 來捕獲。

最重要的是,除了這種方式外,我們可以還使用try/catch/finally,就像我們使用同步函數(shù)所做的一樣。

  1. async function toUppercase(string) { 
  2.   if (typeof string !== "string") { 
  3.     throw TypeError("Wrong type given, expected a string"); 
  4.   } 
  5.  
  6.   return string.toUpperCase(); 
  7.  
  8. async function consumer() { 
  9.   try { 
  10.     await toUppercase(98); 
  11.   } catch (error) { 
  12.     console.error(error.message); 
  13.   } finally { 
  14.     console.log("Always runs!"); 
  15.   } 
  16.  
  17. consumer();  

輸出:

  1. Wrong type given, expected a string 
  2. Always runs! 

使用 async generators 來處理錯誤

JavaScript中的async generators是能夠生成 Promises 而不是簡單值的生成器函數(shù)。

  1. async function* asyncGenerator() { 
  2.   yield 33; 
  3.   yield 99; 
  4.   throw Error("Something went wrong!"); // Promise.reject 

基于 Promise,此處適用于錯誤處理的相同規(guī)則。在異步生成器中 throw 將會觸發(fā) Promise 的reject,我們可以使用catch對其進行攔截。

為了使用異步生成器的 Promise,我們可以這樣做:

  • then 方法
  • 異步遍歷

從上面我們知道,在兩次調(diào)用 yield之后,下一次會拋出一個異常:

  1. const go = asyncGenerator(); 
  2.  
  3. go.next().then(value => console.log(value)); 
  4. go.next().then(value => console.log(value)); 
  5. go.next().catch(reason => console.error(reason.message)); 

輸出結(jié)果:

  1. { value: 33, done: false } 
  2. { value: 99, done: false } 
  3. Something went wrong! 

別一種是使用 異步遍歷與for await...of:

  1. async function* asyncGenerator() { 
  2.   yield 33; 
  3.   yield 99; 
  4.   throw Error("Something went wrong!"); // Promise.reject 
  5.  
  6. async function consumer() { 
  7.   for await (const value of asyncGenerator()) { 
  8.     console.log(value); 
  9.   } 
  10.  
  11. consumer(); 

有了 async/await 我們可以使用 try/catch 來捕獲異常:

  1. async function* asyncGenerator() { 
  2.   yield 33; 
  3.   yield 99; 
  4.   throw Error("Something went wrong!"); // Promise.reject 
  5.  
  6. async function consumer() { 
  7.   try { 
  8.     for await (const value of asyncGenerator()) { 
  9.       console.log(value); 
  10.     } 
  11.   } catch (error) { 
  12.     console.error(error.message); 
  13.   } 
  14.  
  15. consumer(); 

輸出結(jié)果:

  1. 33 
  2. 99 
  3. Something went wrong! 

從異步生成器函數(shù)返回的迭代器對象也具有throw()方法,非常類似于其同步副本。在此處的迭代器對象上調(diào)用throw()不會引發(fā)異常,但是會被Promise拒絕

  1. async function* asyncGenerator() { 
  2.   yield 33; 
  3.   yield 99; 
  4.   yield 11; 
  5.  
  6. const go = asyncGenerator(); 
  7.  
  8. go.next().then(value => console.log(value)); 
  9. go.next().then(value => console.log(value)); 
  10.  
  11. go.throw(Error("Let's reject!")); 
  12.  
  13. go.next().then(value => console.log(value)); // value is undefined 

要從外部處理這種情況,我們可以做:

  1. go.throw(Error("Let's reject!")).catch(reason => console.error(reason.message)); 

Node 中的錯誤處理

Node 中的同步錯誤處理

Node.js 中的同步錯誤處理與到目前為止所看到的并沒有太大差異。對于同步,使用 try/catch/finally 就可以很好的工作了。

Node.js 中的異步錯誤處理:回調(diào)模式

對于異步代碼,Node.js 主要使用這兩種方式:

  • 回調(diào)模式
  • event emitters

在回調(diào)模式中,異步 Node.js API 接受一個函數(shù),該函數(shù)通過事件循環(huán)處理,并在調(diào)用堆棧為空時立即執(zhí)行。

考慮以下代碼:

  1. const { readFile } = require("fs"); 
  2.  
  3. function readDataset(path) { 
  4.   readFile(path, { encoding: "utf8" }, function(error, data) { 
  5.     if (error) console.error(error); 
  6.     // do stuff with the data 
  7.   }); 

我們可以看到,這里處理錯誤的方式是使用了回調(diào):

  1. // 
  2. function(error, data) { 
  3.     if (error) console.error(error); 
  4.     // do stuff with the data 
  5.   } 
  6. // 

如果使用fs.readFile讀取給定路徑而引起任何錯誤,我們將獲得一個錯誤對象。

在這一點上,我們可以:

  • 簡單的把對象錯誤打出來
  • 拋出錯誤
  • 把錯誤傳到另一個回調(diào)

我們可以拋出一個異常

  1. const { readFile } = require("fs"); 
  2.  
  3. function readDataset(path) { 
  4.   readFile(path, { encoding: "utf8" }, function(error, data) { 
  5.     if (error) throw Error(error.message); 
  6.     // do stuff with the data 
  7.   }); 

但是,與 DOM 中的事件和定時器一樣,此異常將使程序崩潰。通過try/catch捕獲它是不起作用的:

  1. const { readFile } = require("fs"); 
  2.  
  3. function readDataset(path) { 
  4.   readFile(path, { encoding: "utf8" }, function(error, data) { 
  5.     if (error) throw Error(error.message); 
  6.     // do stuff with the data 
  7.   }); 
  8.  
  9. try { 
  10.   readDataset("not-here.txt"); 
  11. } catch (error) { 
  12.   console.error(error.message); 

如果我們不想使程序崩潰,則將錯誤傳遞給另一個回調(diào)是首選方法:

  1. const { readFile } = require("fs"); 
  2.  
  3. function readDataset(path) { 
  4.   readFile(path, { encoding: "utf8" }, function(error, data) { 
  5.     if (error) return errorHandler(error); 
  6.     // do stuff with the data 
  7.   }); 

這里的errorHandler顧名思義,是一個用于錯誤處理的簡單函數(shù):

  1. function errorHandler(error) { 
  2.   console.error(error.message); 
  3.   // do something with the error: 
  4.   // - write to a log. 
  5.   // - send to an external logger. 

Node.js 中的異步錯誤處理:event emitters

在 Node.js 中所做的大部分工作都是基于事件的。大多數(shù)情況下,emitter object 和一些觀察者進行交互以偵聽消息。

Node.js中的任何事件驅(qū)動模塊(例如net)都擴展了一個名為EventEmitter的根類。

Node.js中的EventEmitter有兩種基本方法:on和emit。

考慮以下簡單的 HTTP 服務(wù)器:

  1. const net = require("net"); 
  2.  
  3. const server = net.createServer().listen(8081, "127.0.0.1"); 
  4.  
  5. server.on("listening"function () { 
  6.   console.log("Server listening!"); 
  7. }); 
  8.  
  9. server.on("connection"function (socket) { 
  10.   console.log("Client connected!"); 
  11.   socket.end("Hello client!"); 
  12. }); 

這里我們來聽兩個事件:listening 和connection。除了這些事件之外,event emitters 還公開一個 error 事件,以防發(fā)生錯誤。

如果在端口80上運行這段代碼,而不是在前面的示例上偵聽,將會得到一個異常:

  1. const net = require("net"); 
  2.  
  3. const server = net.createServer().listen(80, "127.0.0.1"); 
  4.  
  5. server.on("listening"function () { 
  6.   console.log("Server listening!"); 
  7. }); 
  8.  
  9. server.on("connection"function (socket) { 
  10.   console.log("Client connected!"); 
  11.   socket.end("Hello client!"); 
  12. }); 

輸出:

  1. events.js:291 
  2.       throw er; // Unhandled 'error' event 
  3.       ^ 
  4.  
  5. Error: listen EACCES: permission denied 127.0.0.1:80 
  6. Emitted 'error' event on Server instance at: ... 

要捕獲它,我們可以注冊一個error事件處理程序:

  1. server.on("error"function(error) { 
  2.   console.error(error.message); 
  3. }); 

輸出結(jié)果:

  1. listen EACCES: permission denied 127.0.0.1:80 

總結(jié)

在這個指南中,我們介紹了JavaScript的各種錯誤處理,從簡單的同步代碼到高級的異步。在JavaScript程序中,可以通過多種方式來捕獲異常。

同步代碼中的異常是最容易捕獲的。相反,異步中的異常需要一些技巧來處理。

瀏覽器中的新JavaScript API幾乎都偏向 Promise。then/catch/finally或try/catch的模式對于async/await的異常處理變得更加容易。

作者:Valentino Gagliardi 譯者:前端小智 來源:valentinog

 

原文:https://www.valentinog.com/blog/error/

本文轉(zhuǎn)載自微信公眾號「 大遷世界」,可以通過以下二維碼關(guān)注。轉(zhuǎn)載本文請聯(lián)系 大遷世界公眾號。

 

責任編輯:武曉燕 來源: 大遷世界
相關(guān)推薦

2020-09-14 08:35:36

JavaScript編程開發(fā)

2022-11-16 08:41:43

2017-03-08 08:57:04

JavaScript錯誤堆棧

2017-04-06 14:40:29

JavaScript錯誤處理堆棧追蹤

2023-10-26 12:05:14

Golang開發(fā)

2020-08-20 10:16:56

Golang錯誤處理數(shù)據(jù)

2021-09-27 10:04:03

Go程序處理

2021-09-27 15:33:48

Go 開發(fā)技術(shù)

2021-04-14 07:08:14

Nodejs錯誤處理

2024-03-27 08:18:02

Spring映射HTML

2021-04-29 09:02:44

語言Go 處理

2014-11-17 10:05:12

Go語言

2021-05-11 10:01:54

avaScript錯誤處理

2023-12-26 22:05:53

并發(fā)代碼goroutines

2015-10-09 13:54:14

切面編程錯誤處理機制

2009-08-05 16:04:50

2023-10-28 16:30:19

Golang開發(fā)

2010-06-01 16:14:04

2016-09-07 20:28:17

MySQL存儲數(shù)據(jù)庫

2009-06-19 16:20:14

ASP.NET錯誤處理
點贊
收藏

51CTO技術(shù)棧公眾號