<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??碼云GVP開源項目 12k star Uniapp+ElementUI 功能強大 支持多語言、二開方便! 廣告
                我們已經討論了如何處理異常,那么當你在編寫新的函數的時候,怎么才能向調用者傳遞錯誤呢? 最最重要的一點是為你的函數寫好文檔,包括它接受的參數(附上類型和其它約束),返回值,可能發生的錯誤,以及這些錯誤意味著什么。?**如果你不知道會導致什么錯誤或者不了解錯誤的含義,那你的應用程序正常工作就是一個巧合。**?所以,當你編寫新的函數的時候,一定要告訴調用者可能發生哪些錯誤和錯誤的含義。 ### Throw, Callback 還是 EventEmitter 函數有三種基本的傳遞錯誤的模式。 * `throw`以同步的方式傳遞異常--也就是在函數被調用處的相同的上下文。如果調用者(或者調用者的調用者)用了`try/catch`,則異常可以捕獲。如果所有的調用者都沒有用,那么程序通常情況下會崩潰(異常也可能會被`domains`或者進程級的`uncaughtException`捕捉到,詳見下文)。 * Callback 是最基礎的異步傳遞事件的一種方式。用戶傳進來一個函數(callback),之后當某個異步操作完成后調用這個 callback。通常 callback 會以`callback(err,result)`的形式被調用,這種情況下, err和 result必然有一個是非空的,取決于操作是成功還是失敗。 * 更復雜的情形是,函數沒有用 Callback 而是返回一個 EventEmitter 對象,調用者需要監聽這個對象的 error事件。這種方式在兩種情況下很有用。 * 當你在做一個可能會產生多個錯誤或多個結果的復雜操作的時候。比如,有一個請求一邊從數據庫取數據一邊把數據發送回客戶端,而不是等待所有的結果一起到達。在這個例子里,沒有用 callback,而是返回了一個 EventEmitter,每個結果會觸發一個`row`?事件,當所有結果發送完畢后會觸發`end`事件,出現錯誤時會觸發一個`error`事件。 * 用在那些具有復雜狀態機的對象上,這些對象往往伴隨著大量的異步事件。例如,一個套接字是一個EventEmitter,它可能會觸發“connect“,”end“,”timeout“,”drain“,”close“事件。這樣,很自然地可以把”error“作為另外一種可以被觸發的事件。在這種情況下,清楚知道”error“還有其它事件何時被觸發很重要,同時被觸發的還有什么事件(例如”close“),觸發的順序,還有套接字是否在結束的時候處于關閉狀態。 在大多數情況下,我們會把 callback 和 event emitter 歸到同一個“異步錯誤傳遞”籃子里。如果你有傳遞異步錯誤的需要,你通常只要用其中的一種而不是同時使用。 那么,什么時候用`throw`,什么時候用callback,什么時候又用 EventEmitter 呢?這取決于兩件事: * 這是操作失敗還是程序員的失誤? * 這個函數本身是同步的還是異步的。 直到目前,最常見的例子是在異步函數里發生了操作失敗。在大多數情況下,你需要寫一個以回調函數作為參數的函數,然后你會把異常傳遞給這個回調函數。這種方式工作的很好,并且被廣泛使用。例子可參照 NodeJS 的`fs`模塊。如果你的場景比上面這個還復雜,那么你可能就得換用 EventEmitter 了,不過你也還是在用異步方式傳遞這個錯誤。 其次常見的一個例子是像`JSON.parse`這樣的函數同步產生了一個異常。對這些函數而言,如果遇到操作失敗(比如無效輸入),你得用同步的方式傳遞它。你可以拋出(更加常見)或者返回它。 對于給定的函數,如果有一個異步傳遞的異常,那么所有的異常都應該被異步傳遞。可能有這樣的情況,請求一到來你就知道它會失敗,并且知道不是因為程序員的失誤。可能的情形是你緩存了返回給最近請求的錯誤。雖然你知道請求一定失敗,但是你還是應該用異步的方式傳遞它。 通用的準則就是?**你即可以同步傳遞錯誤(拋出),也可以異步傳遞錯誤(通過傳給一個回調函數或者觸發EventEmitter的?`error`事件),但是不用同時使用**。以這種方式,用戶處理異常的時候可以選擇用回調函數還是用`try/catch`,但是不需要兩種都用。具體用哪一個取決于異常是怎么傳遞的,這點得在文檔里說明清楚。 差點忘了程序員的失誤。回憶一下,它們其實是Bug。在函數開頭通過檢查參數的類型(或是其它約束)就可以被立即發現。一個退化的例子是,某人調用了一個異步的函數,但是沒有傳回調函數。你應該立刻把這個錯拋出,因為程序已經出錯而在這個點上最好的調試的機會就是得到一個堆棧信息,如果有內核信息就更好了。 因為程序員的失誤永遠不應該被處理,上面提到的調用者只能用`try/catch`或者回調函數(或者 EventEmitter)其中一種處理異常的準則并沒有因為這條意見而改變。如果你想知道更多,請見上面的 (不要)處理程序員的失誤。 下表以 NodeJS 核心模塊的常見函數為例,做了一個總結,大致按照每種問題出現的頻率來排列: | 函數 | 類型 | 錯誤 | 錯誤類型 | 傳遞方式 | 調用者 | | --- | --- | --- | --- | --- | --- | | `fs.stat` | 異步 | file not found | 操作失敗 | callback | handle | | `JSON.parse` | 同步 | bad user input | 操作失敗 | throw | `try/catch` | | `fs.stat` | 異步 | null for filename | 失誤 | throw | none (crash) | 異步函數里出現操作錯誤的例子(第一行)是最常見的。在同步函數里發生操作失敗(第二行)比較少見,除非是驗證用戶輸入。程序員失誤(第三行)除非是在開發環境下,否則永遠都不應該出現。 _吐槽:程序員失誤還是操作失敗?_ 你怎么知道是程序員的失誤還是操作失敗呢?很簡單,你自己來定義并且記在文檔里,包括允許什么類型的函數,怎樣打斷它的執行。如果你得到的異常不是文檔里能接受的,那就是一個程序員失誤。如果在文檔里寫明接受但是暫時處理不了的,那就是一個操作失敗。 你得用你的判斷力去決定你想做到多嚴格,但是我們會給你一定的意見。具體一些,想象有個函數叫做“connect”,它接受一個IP地址和一個回調函數作為參數,這個回調函數會在成功或者失敗的時候被調用。現在假設用戶傳進來一個明顯不是IP地址的參數,比如`“bob”`,這個時候你有幾種選擇: * 在文檔里寫清楚只接受有效的IPV4的地址,當用戶傳進來`“bob”`的時候拋出一個異常。強烈推薦這種做法。 * 在文檔里寫上接受任何string類型的參數。如果用戶傳的是`“bob”`,觸發一個異步錯誤指明無法連接到`“bob”`這個IP地址。 這兩種方式和我們上面提到的關于操作失敗和程序員失誤的指導原則是一致的。你決定了這樣的輸入算是程序員的失誤還是操作失敗。通常,用戶輸入的校驗是很松的,為了證明這點,可以看`Date.parse`這個例子,它接受很多類型的輸入。但是對于大多數其它函數,我們強烈建議你偏向更嚴格而不是更松。你的程序越是猜測用戶的本意(使用隱式的轉換,無論是JavaScript語言本身這么做還是有意為之),就越是容易猜錯。本意是想讓開發者在使用的時候不用更加具體,結果卻耗費了人家好幾個小時在Debug上。再說了,如果你覺得這是個好主意,你也可以在未來的版本里讓函數不那么嚴格,但是如果你發現由于猜測用戶的意圖導致了很多惱人的bug,要修復它的時候想保持兼容性就不大可能了。 所以如果一個值怎么都不可能是有效的(本該是string卻得到一個`undefined`,本該是string類型的IP但明顯不是),你應該在文檔里寫明是這不允許的并且立刻拋出一個異常。只要你在文檔里寫的清清楚楚,那這就是一個程序員的失誤而不是操作失敗。立即拋出可以把Bug帶來的損失降到最小,并且保存了開發者可以用來調試這個問題的信息(例如,調用堆棧,如果用內核文件還可以得到參數和內存分布)。 那么?`domains`?和?`process.on('uncaughtException')`?呢? 操作失敗總是可以被顯示的機制所處理的:捕獲一個異常,在回調里處理錯誤,或者處理EventEmitter的“error”事件等等。`Domains`以及進程級別的`‘uncaughtException’`主要是用來從未料到的程序錯誤恢復的。由于上面我們所討論的原因,這兩種方式都不鼓勵。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看