成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

不用try catch,如何機智的捕獲錯誤

開發(fā)
這是多個feature組合使用后實現的神奇效果,在React源碼中被廣泛使用。當我讀源碼看到這里時,心情經歷了:懵逼 -- 困惑 -- 沉思 -- 查文檔 -- 豁然開朗,看完此文,相信你也會發(fā)出感嘆:還能這么玩?

[[344042]]

起源
我們知道,React中有個特性Error Boundary,幫助我們在組件發(fā)生錯誤時顯示“錯誤狀態(tài)”的UI。

為了實現這個特性,就一定需要捕獲到錯誤。

所以在React源碼中,所有用戶代碼都被包裹在一個方法中執(zhí)行。

類似如下: 

  1. function wrapper(func) { 
  2.   try { 
  3.     func(); 
  4.   } catch(e) { 
  5.     // ...處理錯誤 
  6.   } 

比如觸發(fā)componentDidMount時:

  1. wrapper(componentDidMount); 

本來一切都很完美,但是React作為世界級前端框架,受眾廣泛,凡事都講究做到極致。

這不,有人提issue:

  1. 你們這樣在try catch中執(zhí)行用戶代碼會讓瀏覽器調試工具的Pause on exceptions失效。 

Pause on exceptions失效的來龍去脈
Pause on exceptions是什么?

他是瀏覽器調試工具source面板的一個功能。

開啟該功能后,在運行時遇到會拋出錯誤的代碼,代碼的執(zhí)行會自動停在該行,就像在該行打了斷點一樣。

比如,執(zhí)行如下代碼,并開啟該功能:

  1. let a = c; 

代碼的執(zhí)行會在該行暫停。

這個功能可以很方便的幫我們發(fā)現未捕獲的錯誤發(fā)生的位置。

但是,當React將用戶代碼包裹在try catch后,即使代碼拋出錯誤,也會被catch。

Pause on exceptions無法在拋出錯誤的用戶代碼處暫停,因為error已經被React catch了。

除非我們進一步開啟Pause on caught exceptions。

開啟該功能,使代碼在捕獲的錯誤發(fā)生的位置暫停。

如何解決
對用戶來說,我寫在componentDidMount中的代碼明明未捕獲錯誤,可是錯誤發(fā)生時Pause on exceptions卻失效了,確實有些讓人困惑。

所以,在生產環(huán)境,React繼續(xù)使用try catch實現wrapper。

而在開發(fā)環(huán)境,為了更好的調試體驗,需要重新實現一套try catch機制,包含如下功能:

  • 捕獲用戶代碼拋出的錯誤,使Error Boundary功能正常運行
  • 不捕獲用戶代碼拋出的錯誤,使Pause on exceptions不失效

這看似矛盾的功能,React如何機智的實現呢?

如何“捕獲”錯誤
讓我們先實現第一點:捕獲用戶代碼拋出的錯誤。

但是不能使用try catch,因為這會讓Pause on exceptions失效。

解決辦法是:監(jiān)聽window的error事件。

根據GlobalEventHandlers.onerror MDN[1],該事件可以監(jiān)聽到兩類錯誤:

  • js運行時錯誤(包括語法錯誤)。window會觸發(fā)ErrorEvent接口的error事件
  • 資源(如<img>或<script>)加載失敗錯誤。加載資源的元素會觸發(fā)Event接口的error事件,可以在window上捕獲該錯誤

實現開發(fā)環(huán)境使用的wrapperDev:

  1. // 開發(fā)環(huán)境wrapper 
  2. function wrapperDev(func) { 
  3.   function handleWindowError(error) { 
  4.     // 收集錯誤交給Error Boundary處理 
  5.   } 
  6.  
  7.   window.addEventListener('error', handleWindowError); 
  8.   func(); 
  9.   window.removeEventListener('error', handleWindowError); 

當func執(zhí)行時拋出錯誤,會被handleWindowError處理。

但是,對比生產環(huán)境wrapperPrd內func拋出的錯誤會被catch,不會影響后續(xù)代碼執(zhí)行。

  1. function wrapperPrd(func) { 
  2.   try { 
  3.     func(); 
  4.   } catch(e) { 
  5.     // ...處理錯誤 
  6.   } 

開發(fā)環(huán)境func內如果拋出錯誤,代碼的執(zhí)行會中斷。

比如執(zhí)行如下代碼,finish會被打印。

  1. wrapperPrd(() => {throw Error(123)}) 
  2. console.log('finish'); 

但是執(zhí)行如下代碼,代碼執(zhí)行中斷,finish不會被打印。

  1. wrapperDev(() => {throw Error(123)}) 
  2. console.log('finish'); 

如何在不捕獲用戶代碼拋出錯誤的前提下,又能讓后續(xù)代碼的執(zhí)行不中斷呢?

如何讓代碼執(zhí)行不中斷
答案是:通過dispatchEvent觸發(fā)事件回調,在回調中調用用戶代碼。

根據EventTarget.dispatchEvent MDN[2]:

不同于DOM節(jié)點觸發(fā)的事件(比如click事件)回調是由event loop異步觸發(fā)。

通過dispatchEvent觸發(fā)的事件是同步觸發(fā),并且在事件回調中拋出的錯誤不會影響dispatchEvent的調用者(caller)。

讓我們繼續(xù)改造wrapperDev。

首先創(chuàng)建虛構的DOM節(jié)點、事件對象、虛構的事件類型:

  1. // 創(chuàng)建虛構的DOM節(jié)點 
  2. const fakeNode = document.createElement('fake'); 
  3. // 創(chuàng)建event 
  4. const event = document.createEvent('Event'); 
  5. // 創(chuàng)建虛構的event類型 
  6. const evtType = 'fake-event'

初始化事件對象,監(jiān)聽事件。在事件回調中調用用戶代碼。觸發(fā)事件:

  1. function callCallback() { 
  2.   fakeNode.removeEventListener(evtType, callCallback, false);  
  3.   func(); 
  4.  
  5. // 監(jiān)聽虛構的事件類型 
  6. fakeNode.addEventListener(evtType, callCallback, false); 
  7.  
  8. // 初始化事件 
  9. event.initEvent(evtType, falsefalse); 
  10.  
  11. // 觸發(fā)事件 
  12. fakeNode.dispatchEvent(event); 

完整流程如下:

  1. function wrapperDev(func) { 
  2.   function handleWindowError(error) { 
  3.     // 收集錯誤交給Error Boundary處理 
  4.   } 
  5.    
  6.   function callCallback() { 
  7.     fakeNode.removeEventListener(evtType, callCallback, false);  
  8.     func(); 
  9.   } 
  10.    
  11.   const event = document.createEvent('Event'); 
  12.   const fakeNode = document.createElement('fake'); 
  13.   const evtType = 'fake-event'
  14.  
  15.   window.addEventListener('error', handleWindowError); 
  16.   fakeNode.addEventListener(evtType, callCallback, false); 
  17.  
  18.   event.initEvent(evtType, falsefalse); 
  19.    
  20.  
  21.   fakeNode.dispatchEvent(event); 
  22.    
  23.   window.removeEventListener('error', handleWindowError); 

當我們調用:

  1. wrapperDev(() => {throw Error(123)}) 

會依次執(zhí)行:

  • dispatchEvent觸發(fā)事件回調callCallback
  • 在callCallback內執(zhí)行到throw Error(123),拋出錯誤
  • callCallback執(zhí)行中斷,但調用他的函數會繼續(xù)執(zhí)行。
  • Error(123)被window error handler捕獲用于Error Boundary

其中步驟2使Pause on exceptions不會失效。

步驟3、4使得錯誤被捕獲,且不會阻止后續(xù)代碼執(zhí)行,模擬了try catch的效果。

總結
不得不說,React這波操作真細啊。

我們實現的迷你wrapper還有很多不足,比如:

 

  • 沒有針對不同瀏覽器的兼容
  • 沒有考慮其他代碼也觸發(fā)window error handler

React源碼的完整版wrapper,見這里[3]參考資料

 

[1]

GlobalEventHandlers.onerror MDN: https://developer.mozilla.org/zh-CN/docs/Web/API/GlobalEventHandlers/onerror

[2]

EventTarget.dispatchEvent MDN: https://developer.mozilla.org/zh-CN/docs/Web/API/EventTarget/dispatchEvent

[3]

這里: https://github.com/facebook/react/blob/master/packages/shared/invokeGuardedCallbackImpl.js#L63-L237

 

 

責任編輯:姜華 來源: 魔術師卡頌
相關推薦

2021-01-05 07:54:55

事項trycatch

2017-11-02 15:26:10

JavaScriptasync錯誤

2023-09-07 07:53:21

JavaScriptGoRust

2020-08-24 13:35:59

trycatchJava

2024-11-13 01:00:18

asyncawait?編程

2025-04-29 08:05:00

JavaScript錯誤處理開發(fā)

2025-02-12 12:00:00

前端try-catchJavaScrip

2024-06-25 10:37:11

2023-11-13 17:01:26

C++編程

2024-11-28 08:33:18

前端代碼報錯

2024-05-24 08:59:15

2009-12-02 19:56:33

PHP中try{}ca

2021-03-31 11:52:24

try-catch-fJava代碼

2022-06-16 10:37:09

asyncawait

2020-06-15 08:12:51

try catch代碼處理器

2023-05-16 15:32:45

JavaScriptWeb前端工程師

2025-06-25 08:15:00

JavaScrip異步編程代碼

2025-01-16 12:00:00

try-catchfor循環(huán)

2025-03-10 08:10:00

安全賦值運算符ECMAScript編碼

2010-12-21 14:08:50

PowerShell
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 亚洲精色| 一区二区三区视频在线 | 国产精品一区二区视频 | 精品久久国产 | 日韩高清成人 | 99精品在线 | 99视频 | 在线国产小视频 | 日本久久福利 | 国产综合网址 | 欧美国产日韩在线 | a级片在线观看 | 亚洲一区日韩 | 色婷婷影院 | 天天综合国产 | 久久99蜜桃综合影院免费观看 | 国产成人免费视频网站视频社区 | www97影院| 青青草原精品99久久精品66 | 亚洲视频在线观看免费 | 午夜精品久久久久久久久久久久久 | www.亚洲成人网 | 男人的天堂在线视频 | www精品美女久久久tv | 日批免费看 | 天天射网站 | 欧美h版| 91人人视频在线观看 | 第一福利社区1024 | 亚洲一二三区精品 | 91资源在线 | 91社区在线高清 | 午夜影院在线免费观看视频 | 成人一区二区视频 | 伊人焦久影院 | 亚洲一区二区三区在线 | 欧美一卡二卡在线 | 九九在线精品视频 | 综合精品 | 日韩一区二区三区四区五区 | 黄色大片免费播放 |