1. 如何更好地用javaScript中的異常拋出
雖然最近幾年JavaScript有了很多進步,但是相比於其它語言的開發者,JavaScript開發者仍然只有少得可憐的調試工具。因此在JavaScript中throw
error就顯得比其它語言更有價值。我們可以用throw關鍵字來拋出一個對象。我們可以拋出任何類型的對象,不過Error對象是最常用的:
throw new Error("Something bad happened.")
當我們用這樣的方式拋出錯誤,而這個錯誤又不被try-catch捕獲時,瀏覽器就會用其通常的方式顯示上面的錯誤信息(Something
bad
happened)。在IE里會在瀏覽器的左下角出現一個小圖標,當雙擊圖標時會彈出一個帶著上面錯誤提示的對話框;安裝有Firebug插件的火狐瀏覽器會在控制台顯示錯誤信息;Safar和Chrome會在Web
Inspector中顯示;Opera會在錯誤控制台顯示。一句話,它們會像你沒有拋出錯誤時一樣處理。但不同的是它會通過瀏覽器向你提供具體的信息,而不是一個發生錯誤的行列號。你可以為錯誤信息加入任何需要的信息,來幫你成功解決問題。我建議在錯誤信息中提供發生錯誤的函數名稱以及錯誤原因。看下面這個函數:
function addClass(element, className){
element.className += " " + className;
}
這個函數的功能是向一個給定的element加入新的CSS
class(這在JavaScript中非常普遍)。但如果element是null的時候會發生什麼?你會得到一個這樣的錯誤提示「object
expected」,很隱晦。然後你需要查看執行堆棧(如果瀏覽器支持這個功能)來准確定位錯誤的源頭。如果我們拋出一個錯誤調試就變得簡單了:
function addClass(element, className){
if (element != null && typeof element.className == "string"){
element.className += " " + className;
} else {
throw new Error("addClass(): First arg must be a DOM element.");
}
}
先不討論如何精確的判斷對象是否是一個DOM
element,這個方法現在能夠在非法的element參數傳入時提供一個更明確的錯誤信息。看到了如此詳盡的錯誤描述你就能立刻找到錯誤的源頭了。我習慣把throw
error看作是貼一個任務貼紙,告訴我錯誤的原因。
懂得了如何throw error只是事情的一半;懂得何時throw
error則是另一半。因為JavaScript並不對參數進行類型檢查,許多開發者都錯誤的認為他們應該在所有的函數中進行該檢查。那樣的話是不實際的,而且會降低腳本的執行效率。問題的關鍵在於找到最有可能出錯的代碼部分,並且只在那裡throw
error。一句話就是只在已經發生error的地方throw error。
如果一個函數只被一個已知的實體調用,那麼錯誤檢查基本上是沒有必要的(例如私有函數就是這樣);如果你不能事先確定所有函數被調用的地點,那麼你需要進行錯誤檢查並throw自己的error。throw
error最好的地方是功能函數,那些是腳本環境基本組成部分的,而且可以在任意地點被調用的函數。JavaScript的庫函數就是這樣的例子。
所有JavaScript的庫函數都應當為已知的錯誤條件從它們的公共介面throw
error。對於YUI,jQuery以及Dojo等等,我們無法確定會在何時何處調用它們的庫函數。所以當你犯錯時對你進行提示就是這些庫函數的任務。為什麼呢?因為你不可能到庫函數內部去找出錯誤所在。error的調用堆棧應當終止於庫函數介面,不要再深入。沒有什麼比在12層函數嵌套中尋找錯誤更遭的事了;庫函數開發人員有責任預防這種事情的發生。
這一條同樣適用於私有的JavaScript庫函數。許多Web應用程序都有它們自己專屬的JavaScript庫,可能是通過這些庫來構建的,也可能是用庫來代替公共的操作。庫函數的作用是降低開發難度,這是通過向人們提供其抽象表達而不是復雜的實現細節來實現的。throw
error可以讓這些復雜的實現隱藏在安全的地方不被開發者發現。
JavaScript同樣提供了try-catch語句,用來在瀏覽器處理之前捕獲被throw的error。開發者常常會為到底是僅僅throw
error還是用try-catch將其捕獲而猶豫不決。我們應當只在程序棧的最底層throw
error,就像前面提到的,最典型的就是JavaScript庫函數。所有應用程序都應當在邏輯上具有處理error的能力,因此應當在底層模塊中捕獲error。
在應用程序邏輯中我們總是知道為什麼要調用某個函數,因此它們非常適合處理error。有一點要引起注意,就是永遠不要在try-catch結構中使用空的catch語句;你應當用某種方法處理錯誤。這鍾處理在開發中和最終生產時會有些不同,但必須進行處理。當錯誤發生時,不應當僅僅將其包裹在try-catch里不管——這是掩蓋錯誤而不是解決錯誤。
在JavaScript中throw error是一門藝術。在代碼中找到適當的throw error的地點會花費一些時間。不過一旦你找到了這些地點,你的調試時間就會大大降低,而你對代碼的滿意度會獲得提升。
2. JavaScript錯誤提示:Maximum call stack size exceeded
使用window.onerror指定錯誤處理函數
使用Javascript中的try catch throw處理異常
Error.message是IE和FireFox都支持的屬性
IE支持description 和 number屬性
FF支持fileName lineNumber 和 stack 屬性
由於Javascript是弱類型的語言
所以在catch部分只能catch一次,不能像C#這樣的語言可以寫多個catch,catch不同類型的exception
3. jsprint組件異常怎麼處理
有三種類型的編程錯誤:(1)語法錯誤和(2)運行時錯誤(3)邏輯錯誤:
語法錯誤:
語法錯誤,也被稱為解析錯誤,在編譯時進行傳統的編程和雹語言,並出現在JavaScript解釋時。
例如,下面一行將導致一個語法錯誤,因為它缺少一個右括弧:
[js] view plainprint?
<script type="text/javascript">
<!--
window.print(;
//-->
</script>
[js] view plainprint?
<script type="text/javascript">
<!--
window.print(;
//-->
</script>
當一個語法錯誤在JavaScript中出現,只有在同一個線程中包含的語法錯誤的影響,在其他線程的代碼被執行;代碼依賴於包含錯誤的代碼不會被執行。
運行時錯誤:
執行(編譯/解釋後)在運行時錯誤,也被稱為異常,會引發。
例如,下面一行將導致運行時錯誤,因為這里的語法是正確的,但在運行時它正試圖調用非存在的方法:
[js] view plainprint?
<script type="text/javascript">
<!--
window.printme();
//-->
</script>
[js] view plainprint?
<script type="text/javascript">
<!--
window.printme();
//-->
</script>
例外情況也影響到它們發生的線程,允許其他JavaScript線程繼續正常執行。
邏輯錯誤:
邏輯錯誤可能是最困難的類型的錯誤跟蹤。這些錯誤是不是一個語法或運行時錯誤的結果。相反,當發生一個錯誤的驅動腳本邏輯,你沒有得到所期望的結果。
你可能無法抓到這神高些錯誤,因為這取決於程序是什麼類型的邏輯是基於業務需求。
try...catch...finally 語句:
JavaScript的最新版本中添加的異常處理能力。JavaScript實現 try ... catch... finally結構以及拋出操作來處理異常。
你可以捕獲程序員生成和運行時異常,但不能捕獲JavaScript語法錯誤。
這里是 try...catch...finally 塊語法:
[js] view plainprint?
<script type="text/javascript">
<!--
try {
// Code to run
[break;]
} catch ( e ) {
// Code to run if an exception occurs
[break;]
}[ finally {
// Code that is always executed regardless of
// an exception occurring
}]
//-->
</script>
try塊必須後跟只有一個catch塊或者一個finally塊(或兩者之一)。當一個異常在try塊時,除被放置在e和catch塊被執行。 try/catch語句後的可選finally塊無條件喚瞎帆地執行。
示例:
下面是一個例子,我們正試圖調用一個不存在的函數,這將引發異常。讓我們來看看它的行為,不具有try ... catch:
[js] view plainprint?
<html>
<head>
<script type="text/javascript">
<!--
function myFunc()
{
var a = 100;
alert("Value of variable a is : " + a );
}
//-->
</script>
</head>
<body>
<p>Click the following to see the result:</p>
<form>
<input type="button" value="Click Me" οnclick="myFunc();" />
</form>
</body>
</html>
現在,讓我們嘗試使用 try ... catch 捕獲這個異常,並顯示一個用戶友好的消息。也可以取消此消息,如果要隱藏從用戶這個錯誤。
[js] view plainprint?
<html>
<head>
<script type="text/javascript">
<!--
function myFunc()
{
var a = 100;
try {
alert("Value of variable a is : " + a );
} catch ( e ) {
alert("Error: " + e.description );
}
}
//-->
</script>
</head>
<body>
<p>Click the following to see the result:</p>
<form>
<input type="button" value="Click Me" οnclick="myFunc();" />
</form>
</body>
</html>
可以使用finally塊將永遠try/catch語句後,無條件地執行。下面是一個例子:
[js] view plainprint
<html>
<head>
<script type="text/javascript">
<!--
function myFunc()
{
var a = 100;
try {
alert("Value of variable a is : " + a );
}catch ( e ) {
alert("Error: " + e.description );
}finally {
alert("Finally block will always execute!" );
}
}
//-->
</script>
</head>
<body>
<p>Click the following to see the result:</p>
<form>
<input type="button" value="Click Me" οnclick="myFunc();" />
</form>
</body>
</html>
throw 語句:
可以使用throw語句來提高你的內置異常或自定義異常。後來這些異常可以被捕獲並可以採取適當的行動。
以下是表示throw語句的用法的例子。
[js] view plainprint
<html>
<head>
<script type="text/javascript">
<!--
function myFunc()
{
var a = 100;
var b = 0;
try{
if ( b == 0 ){
throw( "Divide by zero error." );
}else{
var c = a / b;
}
}catch ( e ) {
alert("Error: " + e );
}
}
//-->
</script>
</head>
<body>
<p>Click the following to see the result:</p>
<form>
<input type="button" value="Click Me" οnclick="myFunc();" />
</form>
</body>
</html>
您可以使用字元串,整數,布爾或對象在一個函數拋出一個異常,那麼可以捕捉例外在相同的函數,我們在上面做了,或者使用try ... catch塊在其他的函數。
onerror() 語法
onerror事件處理程序是第一個特點,方便JavaScript處理錯誤。錯誤事件被觸發窗口對象,每當一個異常頁面上出現。例如:
[js] view plainprint
<html>
<head>
<script type="text/javascript">
<!--
window.onerror = function () {
alert("An error occurred.");
}
//-->
</script>
</head>
<body>
<p>Click the following to see the result:</p>
<form>
<input type="button" value="Click Me" οnclick="myFunc();" />
</form>
</body>
</html>
onerror事件處理程序提供了三條信息,以找出錯誤的確切性質:
錯誤消息 . 瀏覽器將顯示給定的錯誤相同的消息
URL . 在發生錯誤的文件
行號. 在導致錯誤的URL給出的行號
這里是例子來說明如何提取此信息
[js] view plainprint
<html>
<head>
<script type="text/javascript">
<!--
window.onerror = function (msg, url, line) {
alert("Message : " + msg );
alert("url : " + url );
alert("Line number : " + line );
}
//-->
</script>
</head>
<body>
<p>Click the following to see the result:</p>
<form>
<input type="button" value="Click Me" οnclick="myFunc();" />
</form>
</body>
</html>
可以顯示在任何方式,你覺得這是更好的提取信息。
可以使用onError方法來顯示錯誤消息的情況下沒有在如下載入圖像的任何問題:
[js] view plainprint?
<img src="myimage.gif"
οnerrοr="alert('An error occurred loading the image.')" />
[js] view plainprint
<img src="myimage.gif"
οnerrοr="alert('An error occurred loading the image.')" />
可以使用的onerror許多HTML標記錯誤的情況下顯示相應的信息。
詳解1000+項目數據分析出來的10大JavaScript錯誤
10個用Console來Debug的高級技巧
有瀏覽器的地方就有Fundebug
Debug前端HTML/CSS
轉載於:https://www.cnblogs.com/curationFE/p/handle_javascript_exception.html
相關資源:javascript容錯處理代碼(屏蔽js錯誤)_js容錯處理-其它代碼類資源...
原文鏈接:http://www.cnblogs.com/curationFE/p/handle_javascript_exception.html
點擊閱讀全文
打開CSDN,閱讀體驗更佳
Vue.js中filterBy過濾器導致網頁直接白屏_執筆看墨花開的博客-CSDN博 ...
一用這個方法就白屏,也沒有報錯,後來進vue官網一查才知道,filterBy被替換了 以前是這樣的寫法: <pv-for="user in users | filterBy searchQuery in 'name'">{{user.name}} 1 最新的方法:在 computed 屬性中使用 js 內置方法....
繼續訪問
vue打包js報錯問題(低版本瀏覽器白屏)_十夢她九的博客
在config中的webpack.base.conf.js中,修改編譯配置 entry:{ app:['babel-polyfill','./src/main.js'] } 可解決開發時低版本瀏覽器js報錯問題 方案3 使用組件問題 可能使用的組件存在問題導致打包後報錯 npm update npm run build...
繼續訪問
mvc條碼列印(使用window.print列印條碼)實例
mvc條碼列印(使用window.print列印條碼)實例 代碼: @{ Layout = "~/Views/Shared/_Layout.cshtml"; var types = ViewData["types"].ToString().Trim(); } <script src="@Url.Content("~/Areas/Area/AreaScripts/PrinterInWarehouse.js")" type="text/javascript"></script
繼續訪問
javascript中的contains方法
在研究一個多級菜單聯動的js時,發現contains方法,以前沒有碰到過,不知何意,然後在@司徒正美的博客發現有詳細介紹,暫且摘錄在此。 IE有許多好用的方法,後來都被其他瀏覽器抄襲了,比如這個contains方法。如果A元素包含B元素,則返回true,否則false。唯一不支持這個方法的是IE的死對頭firefox。不過火狐支持compareDocumentPosition(...
繼續訪問
vue框架開發出現頁面空白、白屏的解決方法總匯_晶晶晶晶晶的博客-CSDN...
最後build/webpack.base.conf.js 文件中配置如下: mole.exports= { entry: { app: ["babel-polyfill","./src/main.js"] } }; 6.Vue只在iOS 10出現白屏問題 a:出現變數定義兩次的錯誤描述,如下: SyntaxError:Cannotdeclareale...
繼續訪問
vue頁面白屏的原因及優化_尼克_張的博客_vue 刷新頁面白屏
一、原因:單頁面應用的 html 是靠 js 生成,因為首屏需要載入很大的js文件(app.js vendor.js),所以當網速差的時候會產生一定程度的白屏二、解決辦法:(1)優化 webpack 減少模塊打包體積,code-split 按需載入(2)服務端渲染,在服務端...
繼續訪問
網頁列印javascript:window.print()
在做B/S項目開發時。難免會遇到網頁列印問題! 可以用css控制, @media print .a {display:block} .b {display:hidden} 好像是這樣。把你不想列印的部分class設為b 首先在網頁中添加: VIEWASTEXT> 然後就可以依次加入功能按鈕了: 將這兩塊東西放到就不會列印這些按鈕了。當然要定義no
繼續訪問
window.print() 前端實現網頁列印詳解
轉載: window.print() 前端實現網頁列印詳解_越努力,越幸運!-CSDN博客_window.print
繼續訪問
前端白屏出現的原因及一些解決方式_HuaHua·Li的博客
前端導致白屏的原因: JS問題 常用框架Vue React Angular都是依靠JS進行驅動, 並且單頁面的應用html也是依靠JS生成,在渲染頁面的時候需要載入很大的JS文件( app.js 和vendor.js ),在JS解析載入完成之前無法展示頁面,從而導致了白屏(當網...
繼續訪問
Vue.js項目在IE11白屏報錯_光腳丫思考的博客_ie11打開...
在vue.config.js里添加如下代碼: configureWebpack:config=>{ config.entry.app= ['babel-polyfill','./src/main.js'];}, (四)sockjs-client 還有一種情況是在i.e.打開報錯顯示的sockjs-client錯誤,如下所示: ...
繼續訪問
js常用方法(。。。。不完整)
lastIndexOf();substring();split();slice();splice(); var s="http://www..com"; var i= s.lastIndexOf("."); //16 var unit= s.substring(i); //.com var url= s.substring(0,i); //http://www. ...
繼續訪問
詳解JavaScript中的異常處理方法
當一個語法錯誤在JavaScript中出現,只有在同一個線程中包含的語法錯誤的影響,在其他線程的代碼被執行;代碼依賴於包含錯誤的代碼不會被執行。 運行時錯誤: 執行(編譯/解釋後)在運行時錯誤,也被稱為異常,會引發...
JavaScript 異常處理 詳解
主要介紹了JavaScript 異常處理 詳解,需要的朋友可以參考下
javascript異常處理實現原理詳解
主要介紹了javascript異常處理實現原理詳解,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
最新發布 JavaScript異常處理
JavaScript異常處理 一:異常處理 【錯誤類型】 Error(錯誤)表示系統級的錯誤和程序不必處理的異常,是 JavaScript 運行環境中的內部錯誤或者硬體問題,比如,內存資源不足等。對於這種錯誤,程序基本無能為力,除了退出運行外別無選擇。 系統錯誤 程序錯誤 用戶錯用 【異常】 所謂異常,表示需要捕捉或者需要程序進行處理的地方,它處理的是因為程序設計的瑕疵而引起的問題或者在外的輸入等引起的一般性問題,是程序必須處理的。 【error對象】 創建錯誤 let err = new Erro
繼續訪問
JavaScript代碼異常監控實現過程詳解
JavaScript異常一般有兩方面:語法錯誤和運行時錯誤。兩種錯誤的捕獲和處理方式不同,從而影響具體的方案選型。通常來說,處理JS異常的方案有兩種:try…catch捕獲 和 [removed]捕獲。以下就兩種方案分別分析各自的...
webview載入頁面,JS方法不能載入的問題解決
今天發現在webview,沒有辦法載入JS方法,跳不出那種提示框,就是寫了webview.getSettings().setJavaScriptEnabled(true); 這一句也完全沒用。後來發現網上說的解決方案是用要用到WebChromeClient ,並且重寫它裡面的幾個方法才行。 MyWebChromeClient.java package com.example.webview
繼續訪問
IE與firefox對CSS解析的差異說明
<以下摘自:草堂學社,原文路徑:http://www.cm1314.cn/Article/site/ht/200704/16698.html>列舉了一些常見,新手經常問的問題。舉例並說明解決方法。(內容在下面對應)1.超鏈接訪問過後hover樣式就不出現的問題2.FF下如何使連續長欄位自動換行3.ff下為什麼父容器的高度不能自適應4. IE6的雙倍邊距BUG5. IE6下絕對...
繼續訪問
javascript錯誤處理方式有哪些
今天給大家分享的這篇javascript教程不是很適合初學者觀看,對此不熟悉的朋友建議先看看基礎的知識點。本文主要講述java中的出錯處理,也會涉及到javascript異常處理使用的正反列,以及ajax的非同步處理。 Java的事件驅動機制讓Java更加豐富,瀏覽器好比就是一個事件驅動的機器,錯誤也是一種事件。當一個錯誤發生時,一個事件就在某個點拋 出。理論上,有人會說錯誤是Java中的簡單
繼續訪問
Javascript異常處理機制詳解
在ES3之前js代碼執行的過程中,一旦出現錯誤,整個js代碼都會停止執行,這樣就顯的代碼非常的不健壯。 在Java或C#等一些高級語言中,都提供了異常處理機制,可以處理出現的異常,而不會停止整個應用程序。 從ES3開始,js也提供了類似的異常處理機制,從而讓js代碼變的更健壯,及時執行的過程中出現了異常,也可以讓程序具有了一部分的異常恢復能力。 一、Javascrip
繼續訪問
【JavaScript源代碼】Angular處理未可知異常錯誤的方法詳解.docx
Angular處理未可知異常錯誤的方法詳解 代碼寫得再好,始終都無法完整的處理所有可能產生異常,特別是生產環境中的應用,很大一部分是數據來自用戶、遠程,很難保證所有數據都按程序規定的產生。事實上,除非測試...
JavaScript錯誤處理操作實例詳解
良好的錯誤處理機制可以讓用戶得到及時的提醒,所以讓我們來看看 JavaScript 提供了哪些針對錯誤處理的工具和方法吧O(∩_∩)O~ 1 try-catch 語句 ECMA-262 第 3 版引入了 try-catch 語句,這時 JavaScript 處理異常...
熱門推薦 Javascript異常(exception)處理機制詳解 JS、異常Error屬性
在ES3之前js代碼執行的過程中,一旦出現錯誤,整個js代碼都會停止執行,這樣就顯的代碼非常的不健壯。 在Java或C#等一些高級語言中,都提供了異常處理機制,可以處理出現的異常,而不會停止整個應用程序。 從ES3開始,js也提供了類似的異常處理機制,從而讓js代碼變的更健壯,及時執行的過程中出現了異常,也可以讓程序具有了一部分的異常恢復能力。 一、Javascr
繼續訪問
JS高級調試技巧:捕獲和分析 JavaScript Error詳解
前端工程師都知道 JavaScript 有基本的異常處理能力。我們可以 throw new Error(),瀏覽器也會在我們調用 API 出錯時拋出異常。但估計絕大多數前端工程師都沒考慮過收集這些異常信息
JavaScript錯誤處理機制詳解
從ES3開始,js也提供了類似的異常處理機制,從而讓js代碼變的更健壯,即使執行的過程中出現了異常,也可以讓程序具有了一定的異常恢復能力。
繼續訪問
js異常白屏報錯
把錯誤函數替換掉
javascript
4. javascript中非同步操作的異常怎麼處理
一、JavaScript非同步編程的兩個核心難點
非同步I/O、事件驅動使得單線程的JavaScript得以在不阻塞UI的情況下執行網路、文件訪問功能,且使之在後端實現了較高的性能。然而非同步風格也引來了一些麻煩,其中比較核心的問題是:
1、函數嵌套過深
JavaScript的非同步調用基於回調函數,當多個非同步事務多級依賴時,回調函數會形成多級的嵌套,代碼變成
金字塔型結構。這不僅使得代碼變難看難懂,更使得調試、重構的過程充滿風險。
2、異常處理
回調嵌套不僅僅是使代碼變得雜亂,也使得錯誤處理更復雜。這里主要講講異常處理。
二、異常處理
像很多時髦的語言一樣,JavaScript 也允許拋出異常,隨後再用一個try/catch
語句塊捕獲。如果拋出的異常未被捕獲,大多數JavaScript環境都會提供一個有用的堆棧軌跡。舉個例子,下面這段代碼由於'{'為無效JSON
對象而拋出異常。
?
12345678
function JSONToObject(jsonStr) { return JSON.parse(jsonStr);}var obj = JSONToObject('{');//SyntaxError: Unexpected end of input//at Object.parse (native)//at JSONToObject (/AsyncJS/stackTrace.js:2:15)//at Object.<anonymous> (/AsyncJS/stackTrace.js:4:11)
堆棧軌跡不僅告訴我們哪裡拋出了錯誤,而且說明了最初出錯的地方:第4 行代碼。遺憾的是,自頂向下地跟蹤非同步錯誤起源並不都這么直截了當。
非同步編程中可能拋出錯誤的情況有兩種:回調函數錯誤、非同步函數錯誤。
1、回調函數錯誤
如果從非同步回調中拋出錯誤,會發生什麼事?讓我們先來做個測試。
?
1234567
setTimeout(function A() { setTimeout(function B() { setTimeout(function C() { throw new Error('Something terrible has happened!'); }, 0); }, 0);}, 0);
上述應用的結果是一條極其簡短的堆棧軌跡。
?
12
Error: Something terrible has happened!at Timer.C (/AsyncJS/nestedErrors.js:4:13)
等等,A 和B 發生了什麼事?為什麼它們沒有出現在堆棧軌跡中?這是因為運行C 的時候,非同步函數的上下文已經不存在了,A 和B 並不在內存堆棧里。這3
個函數都是從事件隊列直接運行的。基於同樣的理由,利用try/catch
語句塊並不能捕獲從非同步回調中拋出的錯誤。另外回調函數中的return也失去了意義。
?
1234567
try { setTimeout(function() { throw new Error('Catch me if you can!'); }, 0);} catch (e) {console.error(e);}
看到這里的問題了嗎?這里的try/catch 語句塊只捕獲setTimeout函數自身內部發生的那些錯誤。因為setTimeout
非同步地運行其回調,所以即使延時設置為0,回調拋出的錯誤也會直接流向應用程序。
總的來說,取用非同步回調的函數即使包裝上try/catch 語句塊,也只是無用之舉。(特例是,該非同步函數確實是在同步地做某些事且容易出錯。例如,Node
的fs.watch(file,callback)就是這樣一個函數,它在目標文件不存在時會拋出一個錯誤。)正因為此,Node.js
中的回調幾乎總是接受一個錯誤作為其首個參數,這樣就允許回調自己來決定如何處理這個錯誤。
2、非同步函數錯誤
由於非同步函數是立刻返回的,非同步事務中發生的錯誤是無法通過try-catch來捕捉的,只能採用由調用方提供錯誤處理回調的方案來解決。
例如Node中常見的function (err, ...)
{...}回調函數,就是Node中處理錯誤的約定:即將錯誤作為回調函數的第一個實參返回。再比如HTML5中FileReader對象的onerror函數,會被用於處理非同步讀取文件過程中的錯誤。
舉個例子,下面這個Node 應用嘗試非同步地讀取一個文件,還負責記錄下任何錯誤(如「文件不存在」)。
?
1234567
var fs = require('fs'); fs.readFile('fhgwgdz.txt', function(err, data) { if (err) { return console.error(err); }; console.log(data.toString('utf8'));});
客戶端JavaScript 庫的一致性要稍微差些,不過最常見的模式是,針對成敗這兩種情形各規定一個單獨的回調。jQuery 的Ajax
方法就遵循了這個模式。
?
1234
$.get('/data', { success: successHandler, failure: failureHandler});
不管API 形態像什麼,始終要記住的是,只能在回調內部處理源於回調的非同步錯誤。
三、未捕獲異常的處理
如果是從回調中拋出異常的,則由那個調用了回調的人負責捕獲該異常。但如果異常從未被捕獲,又會怎麼樣?這時,不同的JavaScript環境有著不同的游戲規則……
1. 在瀏覽器環境中
現代瀏覽器會在開發人員控制台顯示那些未捕獲的異常,接著返回事件隊列。要想修改這種行為,可以給window.onerror
附加一個處理器。如果windows.onerror 處理器返回true,則能阻止瀏覽器的默認錯誤處理行為。
?
123
window.onerror = function(err) { return true; //徹底忽略所有錯誤};
在成品應用中, 會考慮某種JavaScript 錯誤處理服務, 譬如Errorception。Errorception
提供了一個現成的windows.onerror 處理器,它向應用伺服器報告所有未捕獲的異常,接著應用伺服器發送消息通知我們。
2. 在Node.js 環境中
在Node 環境中,window.onerror 的類似物就是process 對象的uncaughtException 事件。正常情況下,Node
應用會因未捕獲的異常而立即退出。但只要至少還有一個uncaughtException 事件處理
器,Node 應用就會直接返回事件隊列。
?
123
process.on('uncaughtException', function(err) { console.error(err); //避免了關停的命運!});
但是,自Node 0.8.4 起,uncaughtException 事件就被廢棄了。據其文檔所言,對異常處理而言,uncaughtException
是一種非常粗暴的機制,請勿使用uncaughtException,而應使用Domain 對象。
Domain 對象又是什麼?你可能會這樣問。Domain 對象是事件化對象,它將throw 轉化為'error'事件。下面是一個例子。
?
123456789
var myDomain = require('domain').create();myDomain.run(function() { setTimeout(function() { throw new Error('Listen to me!') }, 50);});myDomain.on('error', function(err) { console.log('Error ignored!');});
源於延時事件的throw 只是簡單地觸發了Domain 對象的錯誤處理器。
Error ignored!
很奇妙,是不是?Domain 對象讓throw
語句生動了很多。不管在瀏覽器端還是伺服器端,全局的異常處理器都應被視作最後一根救命稻草。請僅在調試時才使用它。
四、幾種解決方案
下面對幾種解決方案的討論主要集中於上面提到的兩個核心問題上,當然也會考慮其他方面的因素來評判其優缺點。
1、Async.js
首先是Node中非常著名的Async.js,這個庫能夠在Node中展露頭角,恐怕也得歸功於Node統一的錯誤處理約定。
而在前端,一開始並沒有形成這么統一的約定,因此使用Async.js的話可能需要對現有的庫進行封裝。
Async.js的其實就是給回調函數的幾種常見使用模式加了一層包裝。比如我們需要三個前後依賴的非同步操作,採用純回調函數寫法如下:
?
12345678910111213141516
asyncOpA(a, b, (err, result) => { if (err) { handleErrorA(err); } asyncOpB(c, result, (err, result) => { if (err) { handleErrorB(err); } asyncOpB(d, result, (err, result) => { if (err) { handlerErrorC(err); } finalOp(result); }); });});
如果我們採用async庫來做:
?async.waterfall([ (cb) => { asyncOpA(a, b, (err, result) => { cb(err, c, result); }); }, (c, lastResult, cb) => { asyncOpB(c, lastResult, (err, result) => { cb(err, d, result); }) }, (d, lastResult, cb) => { asyncOpC(d, lastResult, (err, result) => { cb(err, result); }); }], (err, finalResult) => { if (err) { handlerError(err); } finalOp(finalResult);});
可以看到,回調函數由原來的橫向發展轉變為縱向發展,同時錯誤被統一傳遞到最後的處理函數中。
其原理是,將函數數組中的後一個函數包裝後作為前一個函數的末參數cb傳入,同時要求:
每一個函數都應當執行其cb參數;cb的第一個參數用來傳遞錯誤。我們可以自己寫一個async.waterfall的實現:
?let async = { waterfall: (methods, finalCb = _emptyFunction) => { if (!_isArray(methods)) { return finalCb(new Error('First argument to waterfall must be an array of functions')); } if (!methods.length) { return finalCb(); } function wrap(n) { if (n === methods.length) { return finalCb; } return function (err, ...args) { if (err) { return finalCb(err); } methods[n](...args, wrap(n + 1)); } } wrap(0)(false); }};
Async.js還有series/parallel/whilst等多種流程式控制制方法,來實現常見的非同步協作。
Async.js的問題:
在外在上依然沒有擺脫回調函數,只是將其從橫向發展變為縱向,還是需要程序員熟練非同步回調風格。
錯誤處理上仍然沒有利用上try-catch和throw,依賴於「回調函數的第一個參數用來傳遞錯誤」這樣的一個約定。
2、Promise方案
ES6的Promise來源於Promise/A+。使用Promise來進行非同步流程式控制制,有幾個需要注意的問題,
把前面提到的功能用Promise來實現,需要先包裝非同步函數,使之能返回一個Promise:
?
12345678910
function toPromiseStyle(fn) { return (...args) => { return new Promise((resolve, reject) => { fn(...args, (err, result) => { if (err) reject(err); resolve(result); }) }); };}
這個函數可以把符合下述規則的非同步函數轉換為返回Promise的函數:
回調函數的第一個參數用於傳遞錯誤,第二個參數用於傳遞正常的結果。接著就可以進行操作了:
?
123456789101112131415
let [opA, opB, opC] = [asyncOpA, asyncOpB, asyncOpC].map((fn) => toPromiseStyle(fn)); opA(a, b) .then((res) => { return opB(c, res); }) .then((res) => { return opC(d, res); }) .then((res) => { return finalOp(res); }) .catch((err) => { handleError(err); });
通過Promise,原來明顯的非同步回調函數風格顯得更像同步編程風格,我們只需要使用then方法將結果傳遞下去即可,同時return也有了相應的意義:
在每一個then的onFullfilled函數(以及onRejected)里的return,都會為下一個then的onFullfilled函數(以及onRejected)的參數設定好值。
如此一來,return、try-catch/throw都可以使用了,但catch是以方法的形式出現,還是不盡如人意。
3、Generator方案
ES6引入的Generator可以理解為可在運行中轉移控制權給其他代碼,並在需要的時候返回繼續執行的函數。利用Generator可以實現協程的功能。
將Generator與Promise結合,可以進一步將非同步代碼轉化為同步風格:
?
1234567891011
function* getResult() { let res, a, b, c, d; try { res = yield opA(a, b); res = yield opB(c, res); res = yield opC(d); return res; } catch (err) { return handleError(err); }}
然而我們還需要一個可以自動運行Generator的函數:
?
2324252627282930
function spawn(genF, ...args) { return new Promise((resolve, reject) => { let gen = genF(...args); function next(fn) { try { let r = fn(); if (r.done) { resolve(r.value); } Promise.resolve(r.value) .then((v) => { next(() => { return gen.next(v); }); }).catch((err) => { next(() => { return gen.throw(err); }) }); } catch (err) { reject(err); } } next(() => { return gen.next(undefined); }); });}
用這個函數來調用Generator即可:
?
1234567
spawn(getResult) .then((res) => { finalOp(res); }) .catch((err) => { handleFinalOpError(err); });
可見try-catch和return實際上已經以其原本面貌回到了代碼中,在代碼形式上也已經看不到非同步風格的痕跡。
類似的功能有co/task.js等庫實現。
4、ES7的async/await
ES7中將會引入async function和await關鍵字,利用這個功能,我們可以輕松寫出同步風格的代碼,
同時依然可以利用原有的非同步I/O機制。
採用async function,我們可以將之前的代碼寫成這樣:
?
12345678910111213
async function getResult() { let res, a, b, c, d; try { res = await opA(a, b); res = await opB(c, res); res = await opC(d); return res; } catch (err) { return handleError(err); }} getResult();
和Generator & Promise方案看起來沒有太大區別,只是關鍵字換了換。
實際上async
function就是對Generator方案的一個官方認可,將之作為語言內置功能。
async function的缺點:
await只能在async function內部使用,因此一旦你寫了幾個async function,或者使用了依賴於async
function的庫,那你很可能會需要更多的async function。
目前處於提案階段的async
function還沒有得到任何瀏覽器或Node.JS/io.js的支持。Babel轉碼器也需要打開實驗選項,並且對於不支持Generator的瀏覽器來說,還需要引進一層厚厚的regenerator
runtime,想在前端生產環境得到應用還需要時間。
以上就是本文的全部內容,希望對大家的學習有所幫助。