亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

前端魔法堂——異常不僅僅是try/catch

bladefury / 3045人閱讀

摘要:我打算分成前端魔法堂異常不僅僅是和前端魔法堂調(diào)用棧,異常實(shí)例中的寶藏兩篇分別敘述內(nèi)置自定義異常類,捕獲運(yùn)行時(shí)異常語法異常網(wǎng)絡(luò)請(qǐng)求異常事件,什么是調(diào)用棧和如何獲取調(diào)用棧的相關(guān)信息。

前言

?編程時(shí)我們往往拿到的是業(yè)務(wù)流程正確的業(yè)務(wù)說明文檔或規(guī)范,但實(shí)際開發(fā)中卻布滿荊棘和例外情況,而這些例外中包含業(yè)務(wù)用例的例外,也包含技術(shù)上的例外。對(duì)于業(yè)務(wù)用例的例外我們別無它法,必須要求實(shí)施人員與用戶共同提供合理的解決方案;而技術(shù)上的例外,則必須由我們碼農(nóng)們手刃之,而這也是我想記錄的內(nèi)容。
?我打算分成《前端魔法堂——異常不僅僅是try/catch》和《前端魔法堂——調(diào)用棧,異常實(shí)例中的寶藏》兩篇分別敘述內(nèi)置/自定義異常類,捕獲運(yùn)行時(shí)異常/語法異常/網(wǎng)絡(luò)請(qǐng)求異常/PromiseRejection事件,什么是調(diào)用棧和如何獲取調(diào)用棧的相關(guān)信息。
?是不是未出發(fā)就已經(jīng)很期待呢?好吧,大家捉緊扶手,老司機(jī)要開車了^_^

概要

?本篇將敘述如下內(nèi)容:

異常還是錯(cuò)誤?它會(huì)如何影響我們的代碼?

內(nèi)置異常類型有哪些?

動(dòng)手寫自己的異常類型吧!

捕獲“同步代碼”中的"運(yùn)行時(shí)異常",用try/catch就夠了。

"萬能"異常捕獲者window.onerror,真的萬能嗎?

Promise.reject也拋異常,怎么辦?

404等網(wǎng)絡(luò)請(qǐng)求異常真心要后之后覺嗎?

一.異常還是錯(cuò)誤?它會(huì)如何影響我們的代碼?

?在學(xué)習(xí)Java時(shí)我們會(huì)被告知異常(Exception)和錯(cuò)誤(Error)是不一樣的,異常是不會(huì)導(dǎo)致進(jìn)程終止從而可以被修復(fù)(try/catch),但錯(cuò)誤將會(huì)導(dǎo)致進(jìn)程終止因此不能被修復(fù)。當(dāng)對(duì)于JavaScript而言,我們要面對(duì)的僅僅有異常(雖然異常類名為Error或含Error字樣),異常的出現(xiàn)不會(huì)導(dǎo)致JavaScript引擎崩潰,最多就是讓當(dāng)前執(zhí)行的任務(wù)終止而已。
?上面說到異常的出現(xiàn)最多就是讓當(dāng)前執(zhí)行的任務(wù)終止,到底是什么意思呢?這里就涉及到Event Loop的原理了,下面我嘗試用代碼大致說明吧。


二.內(nèi)置異常類型有哪些?

?說到內(nèi)置異常類那么必先提到的就是Error這個(gè)祖先類型了,其他所有的內(nèi)置異常類和自定義類都必須繼承它。而它的標(biāo)準(zhǔn)屬性和方法就以下這寥寥幾個(gè)而已

@prop {String} name - 異常名稱
@prop {String} message - 供人類閱讀的異常信息
@prop {Function} constructor - 類型構(gòu)造器
@method toString():String - 輸出異常信息

?由于標(biāo)準(zhǔn)屬性實(shí)在太少,無法提供更有效的信息供開發(fā)者定位異常發(fā)生的位置和重現(xiàn)事故現(xiàn)場(chǎng),因此各瀏覽器廠家均手多多的自己增加些屬性,然后逐漸成了事實(shí)標(biāo)準(zhǔn)。

@prop {String} fileName - 異常發(fā)生的腳本URI
@prop {number} lineNumber - 異常發(fā)生的行號(hào)
@prop {number} columnNumber - 異常發(fā)生的列號(hào)
@prop {String} stack - 異常發(fā)生時(shí)的調(diào)用棧信息,IE10及以上才支持
@method toSource():String - 異常發(fā)生的腳本內(nèi)容

另外巨硬還新增以下兩個(gè)屬性

@prop {String} description - 和message差不多
@prop {number} number - 異常類型的編號(hào),巨硬為每個(gè)異常設(shè)置了一個(gè)唯一的編號(hào)

?那么現(xiàn)在我要實(shí)例化一個(gè)Error對(duì)象,只需調(diào)用Error()new Error()即可;若想同時(shí)設(shè)置message,則改為Error("test")new Error("test")。其實(shí)Error的構(gòu)造函數(shù)簽名是這樣的

@constructor
@param {String=} message - 設(shè)置message屬性
@param {String=} fileName - 設(shè)置fileName屬性
@param {number=} lineNumber - 設(shè)置lineNUmber屬性

現(xiàn)在我們看看具體有哪些內(nèi)置的異常類型吧!

EvalError,調(diào)用eval()時(shí)發(fā)生的異常,已被廢棄只用于向后兼容而已

InternalError,JavaScript引擎內(nèi)部異常,F(xiàn)ireFox獨(dú)門提供的!

RangeError,當(dāng)函數(shù)實(shí)參越界時(shí)發(fā)生,如Array,Number.toExponential,Number.toFixedNumber.toPrecision時(shí)入?yún)⒎欠〞r(shí)。

ReferenceError,當(dāng)引用未聲明的變量時(shí)發(fā)生

SyntaxError,解析時(shí)發(fā)生語法錯(cuò)誤

TypeError,當(dāng)值不是所期待的類型時(shí),null.f()也報(bào)這個(gè)錯(cuò)

URIError,當(dāng)傳遞一個(gè)非法的URI給全局URI處理函數(shù)時(shí)發(fā)生,如decodeURIComponent("%"),即decodeURIComponent,decodeURI,encodeURIComponentencodeURI

三.動(dòng)手寫自己的異常類型吧!

?關(guān)于在StackOverflow上早有人討論如何自定義異常類型了參考
于是我們順手拈來即可

function MyError(message, fileName, lineNumber){
  if (this instanceof MyError);else return new MyError(message, fileName, lineNumber)
  this.message = message || ""
  if (fileName){ this.fileName = fileName }
  if (lineNumber){ this.lineNumber = lineNumber }
}

var proto = MyError.prototype = Object.create(Error.prototype)
proto.name = "MyError"
proto.constructor = MyError

cljs實(shí)現(xiàn)如下

(defn ^export MyError [& args]
  (this-as this
    (if (instance? MyError this)
      (let [ps ["message" "fileName" "lineNumber"]
            idxs (-> (min (count args) (count ps)) range)]
        (reduce
          (fn [accu i]
            (aset accu (nth ps i) (nth args i))
            accu)
          this
          idxs))
      (apply new MyError args))))

(def proto
  (aset MyError "prototype" (.create js/Object (.-prototype Error))))
(aset proto "name" "MyError")
(aset proto "constructor" MyError)
四.捕獲“同步代碼”中的"運(yùn)行時(shí)異常",用try/catch就夠了

?為了防止由于異常的出現(xiàn),導(dǎo)致正常代碼被略過的風(fēng)險(xiǎn),我們習(xí)慣采取try/catch來捕獲并處理異常。

try{
  throw Error("unexpected operation happen...")
}
catch (e){
  console.log(e.message)
}

cljs寫法

(try
  (throw (Error. "unexpected operation happen...")
  (catch e
         (println (.-message e)))))

?很多時(shí)我們會(huì)以為這樣書寫就萬事大吉了,但其實(shí)try/catch能且僅能捕獲“同步代碼”中的"運(yùn)行時(shí)異常"。
1."同步代碼"就是說無法獲取如setTimeout、Promise等異步代碼的異常,也就是說try/catch僅能捕獲當(dāng)前任務(wù)的異常,setTimeout等異步代碼是在下一個(gè)EventLoop中執(zhí)行。

// 真心捕獲不到啊親~!
try{
  setTimeout(function(){
    throw Error("unexpected operation happen...")
  }, 0)
} catch(e){
  console.log(e)
}

2."運(yùn)行時(shí)異常"是指非SyntaxError,也就是語法錯(cuò)誤是無法捕獲的,因?yàn)樵诮馕鯦avaScript源碼時(shí)就報(bào)錯(cuò)了,還怎么捕獲呢~~

// 非法標(biāo)識(shí)符a->b,真心捕獲不到啊親~!
try{
  a->b = 1
} catch(e){
  console.log(e)
}

?這時(shí)大家會(huì)急不可待地問:“異步代碼的異常咋辦呢?語法異常咋辦呢?”在解答上述疑問前,我們先偏離一下,稍微挖挖throw語句的特性。

throw后面可以跟什么?。?/b>

?一般而言我們會(huì)throw一個(gè)Error或其子類的實(shí)例(如throw Error()),其實(shí)我們throw任何類型的數(shù)據(jù)(如throw 1,throw "test",throw true等)。但即使可以拋出任意類型的數(shù)據(jù),我們還是要堅(jiān)持拋出Error或其子類的實(shí)例。這是為什么呢?

try{
  throw "unexpected operation happen..."
} catch(e){
  console.log(e)
}

try{
  throw TypeError("unexpected operation happen...")
} catch(e){
  if ("TypeError" == e.name){
    // Do something1
  }
  else if ("RangeError" == e.name){
    // Do something2
  }
}

?原因顯然易見——異常發(fā)生時(shí)提供信息越全越好,更容易追蹤定位重現(xiàn)問題嘛!

五."萬能"異常捕獲者window.onerror,真的萬能嗎?

?在每個(gè)可能發(fā)生異常的地方都寫上try/catch顯然是不實(shí)際的(另外還存在性能問題),即使是羅嗦如Java我們開發(fā)時(shí)也就是不斷聲明throws,然后在頂層處理異常罷了。那么,JavaScript中對(duì)應(yīng)的頂層異常處理入口又在哪呢?木有錯(cuò),就是在window.onerror??纯捶椒ê灻?/p>

@description window.onerror處理函數(shù)
@param {string} message - 異常信息"
@param {string} source  - 發(fā)生異常的腳本的URI
@param {number} lineno  - 發(fā)生異常的腳本行號(hào)
@param {number} colno   - 發(fā)生異常的腳本列號(hào)
@param {?Error} error   - Error實(shí)例,Safari和IE10中沒有這個(gè)實(shí)參

?這時(shí)我們就可以通過它捕獲除了try/catch能捕獲的異常外,還可以捕獲setTimeout等的異步代碼異常,語法錯(cuò)誤。

window.onerror = function(message, source, lineno, colno, error){
  // Do something you like.
}

setTimeout(function(){ throw Error("oh no!") }, 0)
a->b = 1

?這樣就滿足了嗎?還沒出大殺技呢——屏蔽異常、屏蔽、屏~~
?只有onerror函數(shù)返回true時(shí),異常就不會(huì)繼續(xù)向上拋(否則繼續(xù)上拋就成了Uncaught Error了)。

// 有異常沒問題啊,因?yàn)槲铱床坏絕_^
window.onerror = function(){return true}

?現(xiàn)在回到標(biāo)題的疑問中,有了onerror就可以捕獲所有異常了嗎?答案又是否定的(我的娘啊,還要折騰多久啊~0~)

Chrome中對(duì)于跨域腳本所報(bào)的異常,雖然onerror能夠捕獲,但統(tǒng)一報(bào)Script Error。若要得到正確的錯(cuò)誤信息,則要配置跨域資源共享CORS才可以。

window.onerror實(shí)際上采用的事件冒泡的機(jī)制捕獲異常,并且在冒泡(bubble)階段時(shí)才觸發(fā),因此像網(wǎng)絡(luò)請(qǐng)求異常這些不會(huì)冒泡的異常是無法捕獲的。

Promise.reject產(chǎn)生的未被catch的異常,window.onerror也是無能為力。

六.Promise.reject也拋異常,怎么辦?

?通過Promise來處理復(fù)雜的異步流程控制讓我們得心應(yīng)手,但倘若其中出現(xiàn)異常或Promise實(shí)例狀態(tài)變?yōu)閞ejected時(shí),會(huì)是怎樣一個(gè)狀況,我們又可以如何處理呢?

Promise是如何標(biāo)識(shí)異常發(fā)生的?

?Promise實(shí)例的初始化狀態(tài)是pending,而發(fā)生異常時(shí)則為rejected,而導(dǎo)致狀態(tài)從pending轉(zhuǎn)變?yōu)閞ejected的操作有

調(diào)用Promise.reject類方法

在工廠方法中調(diào)用reject方法

在工廠方法或then回調(diào)函數(shù)中拋異常

// 方式1
Promise.reject("anything you want")

// 方式2
new Promise(function(resolve, reject) { reject("anything you want") })

// 方式3
new Promise(function{ throw "anything you want" })
new Promise(function(r) { r(Error("anything you want" ) }).then(function(e) { throw e })

?當(dāng)Promise實(shí)例從pending轉(zhuǎn)變?yōu)閞ejected時(shí),和之前談?wù)摰疆惓R粯?,要么被捕獲處理,要么繼續(xù)拋出直到成為Uncaught(in promise) Error為止。

異常發(fā)生前就catch

?若在異常發(fā)生前我們已經(jīng)調(diào)用catch方法來捕獲異常,那么則相安無事

new Promise(function(resolve, reject){
  setTimeout(reject, 0)
}).catch(function(e){
  console.log("catch")
  return "bingo"
}).then(function(x){
  console.log(x)
})

// 回顯 bingo
專屬于Promise的頂層異常處理

?若在異常發(fā)生前我們沒有調(diào)用catch方法來捕獲異常,還是可以通過windowunhandledrejection事件捕獲異常的

window.addEventListener("unhandledrejection", function(e){
  // Event新增屬性
  // @prop {Promise} promise - 狀態(tài)為rejected的Promise實(shí)例
  // @prop {String|Object} reason - 異常信息或rejected的內(nèi)容

  // 會(huì)阻止異常繼續(xù)拋出,不讓Uncaught(in promise) Error產(chǎn)生
  e.preventDefault()
})
遲來的catch

?由于Promise實(shí)例可異步訂閱其狀態(tài)變化,也就是可以異步注冊(cè)catch處理函數(shù),這時(shí)其實(shí)已經(jīng)拋出Uncaught(in promise) Error,但我們依然可以處理

var p = new Promise(function(resolve, reject){
  setTimeout(reject, 0)
})
setTimeout(function(){
  p.catch(function(e){
    console.log("catch")
    return "bingo"
  })
}, 1000)

?另外,還可以通過windowrejectionhandled事件監(jiān)聽異步注冊(cè)catch處理函數(shù)的行為

window.addEventListener("rejectionhandled", function(e){
  // Event新增屬性
  // @prop {Promise} promise - 狀態(tài)為rejected的Promise實(shí)例
  // @prop {String|Object} reason - 異常信息或rejected的內(nèi)容

  // Uncaught(in promise) Error已經(jīng)拋出,所以這句毫無意義^_^
  e.preventDefault()
})

注意:只有拋出Uncaught(in promise) Error后,異步catch才會(huì)觸發(fā)該事件。

七.404等網(wǎng)絡(luò)請(qǐng)求異常真心要后之后覺嗎?

?也許我們都遇到報(bào)404網(wǎng)絡(luò)請(qǐng)求異常的情況,然后測(cè)試或用戶保障怎么哪個(gè)哪個(gè)圖標(biāo)沒有顯示。其實(shí)我們我們可以通過以下方式捕獲這類異常

window.addEventListener("error", function(e){
  // Do something
  console.log(e.bubbles) // 回顯false
}, true)

?由于網(wǎng)絡(luò)請(qǐng)求異常不會(huì)冒泡,因此必須在capture階段捕獲才可以。但還有一個(gè)問題是這種方式無法精確判斷異常的HTTP狀態(tài)是404還是500等,因此還是要配合服務(wù)端日志來排查分析才可以。

總結(jié)

?對(duì)異常和如何捕獲異常僅僅是前端智能監(jiān)控中的一小撮知識(shí)點(diǎn),敬請(qǐng)期待后續(xù)另一小撮知識(shí)點(diǎn)《前端魔法堂——調(diào)用棧,異常實(shí)例中的寶藏》吧:D
?尊重原創(chuàng),轉(zhuǎn)載請(qǐng)注明來自:http://www.cnblogs.com/fsjohn... ^_^肥仔John

參考

https://developer.mozilla.org...
https://stackoverflow.com/que...

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/89129.html

相關(guān)文章

  • 前端魔法——調(diào)用棧,異常實(shí)例中的寶藏

    摘要:前言在上一篇前端魔法堂異常不僅僅是中我們描述出一副異常及如何捕獲異常的畫像,但僅僅如此而已。調(diào)用方從右到左的順序?qū)?shù)壓入棧中,在被調(diào)用方執(zhí)行完成后,由被調(diào)用方負(fù)責(zé)清理?xiàng)V械膮?shù)也稱為棧平衡。 前言 ?在上一篇《前端魔法堂——異常不僅僅是try/catch》中我們描述出一副異常及如何捕獲異常的畫像,但僅僅如此而已。試想一下,我們窮盡一切捕獲異常實(shí)例,然后僅僅為告訴用戶,運(yùn)維和開發(fā)人員頁...

    wangjuntytl 評(píng)論0 收藏0
  • 前端錯(cuò)誤監(jiān)控與收集探究

    摘要:這樣很容易造成大的損失,提前做好錯(cuò)誤收集和處理,可以減少損失。 編寫代碼只是做好項(xiàng)目的一小部分,寫代碼難免會(huì)碰到錯(cuò)誤。因此,在項(xiàng)目上線后,我們還需要主動(dòng)對(duì)項(xiàng)目的錯(cuò)誤進(jìn)行收集,不能等用戶發(fā)現(xiàn)錯(cuò)誤,再聯(lián)系我們,我們?cè)偃ヌ幚?。這樣很容易造成大的損失,提前做好錯(cuò)誤收集和處理,可以減少損失。 本人并沒有做過相關(guān)的工作,下面的文章只是我在學(xué)習(xí)中的一點(diǎn)思考和總結(jié),可能有比較多不足和錯(cuò)誤的地方,希望大...

    ZoomQuiet 評(píng)論0 收藏0
  • 阿里云前端周刊 - 第 29 期

    摘要:前端魔法堂異常不僅僅是在學(xué)習(xí)時(shí)我們會(huì)被告知異常和錯(cuò)誤是不一樣的,異常是不會(huì)導(dǎo)致進(jìn)程終止從而可以被修復(fù),但錯(cuò)誤將會(huì)導(dǎo)致進(jìn)程終止因此不能被修復(fù)。 推薦 1. RESTful API 設(shè)計(jì)最佳實(shí)踐 https://blog.philipphauer.de/... 項(xiàng)目資源的URL應(yīng)該如何設(shè)計(jì)?用名詞復(fù)數(shù)還是用名詞單數(shù)?一個(gè)資源需要多少個(gè)URL?用哪種HTTP方法來創(chuàng)建一個(gè)新的資源?可選參數(shù)應(yīng)...

    Jaden 評(píng)論0 收藏0
  • JS魔法:深究JS異步編程模型

    摘要:而同步和異步則是描述另一個(gè)方面。異步將數(shù)據(jù)從內(nèi)核空間拷貝到用戶空間的操作由系統(tǒng)自動(dòng)處理,然后通知應(yīng)用程序直接使用數(shù)據(jù)即可。 前言 ?上周5在公司作了關(guān)于JS異步編程模型的技術(shù)分享,可能是內(nèi)容太干的緣故吧,最后從大家的表情看出這條粉腸到底在說啥?的結(jié)果:(下面是PPT的講義,具體的PPT和示例代碼在https://github.com/fsjohnhuan...上,有興趣就上去看看吧! ...

    idealcn 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<