国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

js錯誤處理權威指北

lykops / 2237人閱讀

摘要:第一個是,是你傳遞給異常的構造函數的參數,比如你可以使用屬性來訪問到該消息第二個參數是異常堆棧跟蹤,非常重要。異常產生后能在后端正確處理是的關鍵部分。我將向你展示自定義構造函數和錯誤代碼的方法,我們可以輕松地將其傳遞給前端或任何調用者。

By Lukas Gisder-Dubé | nov 14, 2018

原文

接著我上一篇文章,我想談談異常。我肯定你之前也聽過——異常是個好東西。一開始,我們害怕異常,畢竟寫bug容易被人噴。其實通過修bug,我們實際上學會了下次開發怎么避免這個bug并且可以做得更好。

在生活中,我們常說吃一塹長一智。但對于打代碼來說,有些不一樣。我們的編譯器和一些工具現在都很智能,不但告訴我們哪里出錯,還幫助我們優化代碼【譯者:eslint之類的】(有可能還會教我們如何修復bug)。

js異常的一般處理方法
throw new Error("something went wrong")

以上代碼將會創建異常實例并停止執行你的腳本,除非你在錯誤回調里做一些處理。當你開始了js開發者的職業生涯,你自己很可能不會這樣做,但是你會在其它的庫里(或者運行時)看到類似‘ReferenceError: fs為定義’這樣的錯誤。

異常對象

異常對象有兩個屬性供我們使用。第一個是message,是你傳遞給異常的構造函數的參數,比如:

new Error("This is the message")

你可以使用message屬性來訪問到該消息:

const myError = new Error(‘please improve your code’)
console.log(myError.message) // please improve your code

第二個參數是異常堆棧跟蹤,非常重要。你可以使用stack屬性來訪問。異常堆棧為你提供歷史記錄(調用堆棧),從中可以查看到哪些文件導致了異常。堆棧頂部也包括了消息,然后是實際的堆棧,從距離異常最近的點開始,然后一直到最外層與異常有關的文件(譯者:調用關系的追溯):

Error: please improve your code
 at Object. (/Users/gisderdube/Documents/_projects/hacking.nosync/error-handling/src/general.js:1:79)
 at Module._compile (internal/modules/cjs/loader.js:689:30)
 at Object.Module._extensions..js (internal/modules/cjs/loader.js:700:10)
 at Module.load (internal/modules/cjs/loader.js:599:32)
 at tryModuleLoad (internal/modules/cjs/loader.js:538:12)
 at Function.Module._load (internal/modules/cjs/loader.js:530:3)
 at Function.Module.runMain (internal/modules/cjs/loader.js:742:12)
 at startup (internal/bootstrap/node.js:266:19)
 at bootstrapNodeJSCore (internal/bootstrap/node.js:596:3)
 
拋出并處理異常

現在單個異常實例沒有任何卵用。例如

new Error("...")

以上代碼并不會觸發任何東西。當異常被拋出,事情就變得有趣了一些。然后,跟上文說的一樣,js引擎停止執行你的腳本,除非你做了異常處理。記住,手動拋出異常,還是由庫拋出異常,抑或是運行時拋出異常,都沒關系。讓我們看看在不同場景如何處理這些異常。

try....catch

這是最簡單的,但是經常被忘記的異常處理方法——多虧了async/await,越來越多人使用它了。它可以用來捕捉各種類型的非異步錯誤。例如:

const a = 5

try {
    console.log(b) // b is not defined, so throws an error
} catch (err) {
    console.error(err) // will log the error with the error stack
}

console.log(a) // still gets executed

如果我們不將console.log(b)包裝在try ... catch塊中,腳本執行將停止。

...finally

有時候有不管有沒有異常,都希望執行的代碼。你可以使用finally。通常,它與try ... catch語句之后只有一行相同,但有時它可能很有用

const a = 5

try {
    console.log(b) // b is not defined, so throws an error
} catch (err) {
    console.error(err) // will log the error with the error stack
} finally {
    console.log(a) // will always get executed
}
異步——回調

異步,這是你在使用js時不得不去考慮的一個主題。當你有個異步方法,并且改方法內部發生異常時,你的腳本會繼續執行,不會立即出現任何異常。當使用回調來處理異步方法的返回時(順便提一下,不提倡使用回調),你通常會接收兩個參數,例如:

myAsyncFunc(someInput, (err, result) => {
    if(err) return console.error(err) // we will see later what to do with the error object.
    console.log(result)
})

如果發生異常,err參數就是那個異常。如果沒有,參數就是undefined或者時null。這樣做很重要,不然如果你試圖訪問result.data時,乳溝發生異常,得到的結果就是undefined。

異步——Promises

處理異步的另一種方法時使用promises。除了代碼更易讀,異常處理也改進了。我們只需要在catch里處理異常就好了,不需要關心怎么捕捉異常。當鏈式調用promises時,catch會捕獲自promise或最后一個catch塊執行以來的所有錯誤。請注意,沒有catch的promises不會終止腳本,但是會降低你的異常信息的可讀性:

(node:7741) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): Error: something went wrong
(node:7741) DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code. */

因此,記得為你的promises加上catch:

Promise.resolve(1)
    .then(res => {
        console.log(res) // 1

        throw new Error("something went wrong")

        return Promise.resolve(2)
    })
    .then(res => {
        console.log(res) // will not get executed
    })
    .catch(err => {
        console.error(err) // we will see what to do with it later
        return Promise.resolve(3)
    })
    .then(res => {
        console.log(res) // 3
    })
    .catch(err => {
        // in case in the previous block occurs another error
        console.error(err)
    })
try … catch?—?again

隨著js引入async / await,我們回到了處理異常的原始方式,使用try ... catch ... finally,這使得處理它們變得輕而易舉:

;(async function() {
    try {
        await someFuncThatThrowsAnError()
    } catch (err) {
        console.error(err) // we will make sense of that later
    }

    console.log("Easy!") // will get executed
})()

由于這和我們處理“普通”同步異常方式一樣,所以如果有需要,更容易使用更大作用域的catch語句。

服務器端異常產生與處理

現在我們有處理異常的工具了,讓我們看看在實際情況中可以用這些工具做些什么。異常產生后能在后端正確處理是app的關鍵部分。這列有幾種處理異常的方法。我將向你展示自定義error構造函數和錯誤代碼的方法,我們可以輕松地將其傳遞給前端或任何API調用者。構建后端的細節不重要,基本思路不變。

我們用Express.js作為路由框架。讓我們考慮一下我們希望獲得最有效的異常處理的結構。我們想要:

一般異常處理,如某種回退,基本上只是說:“有錯誤,請再試一次或聯系我們”。這不是特別好,但至少通知了用戶,app出錯了——而不是無限加載或者白屏。

特殊錯誤處理為用戶提供詳細信息,讓用戶了解有什么問題以及如何解決它們,例如,數據丟失,已存在條目等等。

構建一個自定義error構造函數

我們使用存在的erroe構造函數并且繼承它。繼承在js中是一件危險的事,但是在這里,我覺得非常有用。為什么我們需要它?我們仍然希望堆棧跟蹤為我們提供良好的調試體驗。拓展js自帶的error構造函數就可以繼續使用堆棧跟蹤。我們唯一要做的就是添加代碼和傳遞前端error.code

class CustomError extends Error {
    constructor(code = "GENERIC", status = 500, ...params) {
        super(...params)

        if (Error.captureStackTrace) {
            Error.captureStackTrace(this, CustomError)
        }

        this.code = code
        this.status = status
    }
}

module.exports = CustomError
如何處理路由

完成error的自定義之后,我們需要設置路由結構。正如我所指出的,我們需要一個單點truth來進行異常處理,這意味著對于每個路由,我們都希望具有相同的異常處理行為。express默認是不支持的,因為路由封裝好了。

為了解決這個問題,我們可以實現一個路由處理程序,并把實際的路由邏輯定義為普通函數。這樣,如果路由功能(或任何內部函數)拋出異常,他將返回到路由處理程序,然后可以返回給前端。當后端發生錯誤時,我們用以下格式傳遞給前端——比如一個JSON API:

{
    error: "SOME_ERROR_CODE",
    description: "Something bad happened. Please try again or     contact support."
}

準備好大吃一驚吧,當我說下面這段話時,我的學生總是生氣:

如果你咋看之下不太理解,不用擔心。只要使用一段時間,你就會發現為什么要那樣。

順便說一下,這叫自上而下學習,我非常喜歡。

路由處理程序像這樣子:

const express = require("express")
const router = express.Router()
const CustomError = require("../CustomError")

router.use(async (req, res) => {
    try {
        const route = require(`.${req.path}`)[req.method]

        try {
            const result = route(req) // We pass the request to the route function
            res.send(result) // We just send to the client what we get returned from the route function
        } catch (err) {
            /*
            This will be entered, if an error occurs inside the route function.
            */
            if (err instanceof CustomError) {
                /* 
                In case the error has already been handled, we just transform the error 
                to our return object.
                */

                return res.status(err.status).send({
                    error: err.code,
                    description: err.message,
                })
            } else {
                console.error(err) // For debugging reasons

                // It would be an unhandled error, here we can just return our generic error object.
                return res.status(500).send({
                    error: "GENERIC",
                    description: "Something went wrong. Please try again or contact support.",
                })
            }
        }
    } catch (err) {
        /* 
        This will be entered, if the require fails, meaning there is either 
        no file with the name of the request path or no exported function 
        with the given request method.
        */
        res.status(404).send({
            error: "NOT_FOUND",
            description: "The resource you tried to access does not exist.",
        })
    }
})

module.exports = router

我希望你看下代碼的注釋,我想這比我在這解釋有意義。現在,讓我們看下實際的路由文件長什么樣子:

const CustomError = require("../CustomError")

const GET = req => {
    // example for success
    return { name: "Rio de Janeiro" }
}

const POST = req => {
    // example for unhandled error
    throw new Error("Some unexpected error, may also be thrown by a library or the runtime.")
}

const DELETE = req => {
    // example for handled error
    throw new CustomError("CITY_NOT_FOUND", 404, "The city you are trying to delete could not be found.")
}

const PATCH = req => {
    // example for catching errors and using a CustomError
    try {
        // something bad happens here
        throw new Error("Some internal error")
    } catch (err) {
        console.error(err) // decide what you want to do here

        throw new CustomError(
            "CITY_NOT_EDITABLE",
            400,
            "The city you are trying to edit is not editable."
        )
    }
}

module.exports = {
    GET,
    POST,
    DELETE,
    PATCH,
}

在這些例子中,我沒有對實際請求做任何事情,我只是假裝不同的異常場景。 因此,例如,GET / city將在第3行結束,POST / city將在第8行結束,依此類推。 這也適用于查詢參數,例如 GET / city?startsWith = R. 從本質上講,您將有一個未處理的異常,前端將收到:

{
    error: "GENERIC",
    description: "Something went wrong. Please try again or contact support."
}

或者你手動拋出CustomError,例如:

throw new CustomError("MY_CODE", 400, "Error description")

上述代碼會變成:

{
    error: "GENERIC",
    description: "Something went wrong. Please try again or contact support."
}

現在我們有了這個漂亮的后端設置,我們不再有錯誤日志泄漏到前端,并將始終返回有關出錯的可用信息。

向用戶顯示異常

下一步也是最后一步是管理前端的異常。 在這里,您希望使用第一部分中描述的工具處理前端邏輯本身產生的異常。但是,也必須顯示來自后端的異常。 我們先來看看我們如何顯示異常。 如前所述,我們將在演練中使用React。

把異常保存在react state中

接下來我們要澄清的是具有匹配視覺表示的不同類型的異常。就像在后端一樣,有三種類型:

全局異常,例如,其中一個常見的異常是來自后臺,用戶沒有登錄等。

來自后臺的具體異常,例如,用戶向后臺發送登錄憑證。后臺答復密碼錯誤

前端導致的異常,例如,電子郵箱格式錯誤。

2和3雖然源頭不一樣,但是非常類似并且可以在同樣的state處理。我們來看看在代碼中如何實現。

我們使用react原聲state實現,但是,你可以使用類似MobX或Redux這樣的狀態管理系統。

全局異常

通常,我將這些異常保存在最外層的有狀態組件中并呈現靜態UI元素,這可能是屏幕頂部的紅色橫幅,模態或其他任何內容,設計實現你自己決定。

來看下代碼:

import React, { Component } from "react"

import GlobalError from "./GlobalError"

class Application extends Component {
    constructor(props) {
        super(props)

        this.state = {
            error: "",
        }

        this._resetError = this._resetError.bind(this)
        this._setError = this._setError.bind(this)
    }

    render() {
        return (
            

Handling Errors

) } _resetError() { this.setState({ error: "" }) } _setError(newError) { this.setState({ error: newError }) } } export default Application

正如你所看到的一樣,Application.js中的狀態存在異常。我們也有方法重置和更改異常值。 我們將值和重置方法傳遞給GlobalError組件,在點擊‘x’時,該組件會顯示異常并重置。讓我們來看看 我們將值和reset方法向下傳遞給GlobalError組件:

import React, { Component } from "react"

class GlobalError extends Component {
    render() {
        if (!this.props.error) return null

        return (
            
{this.props.error}   close
) } } export default GlobalError

你可以在第五行看到,如果沒有異常,我們不會渲染任何內容。這可以防止我們始終在頁面上顯示空的紅色框。當然,你可以更改此組件的外觀和行為。例如,你可以使用Timeout替換"x",以便在幾秒鐘后重置異常狀態。

現在,你已準備好在任何地方使用此全局異常狀態,只需從Application.js傳遞_setError,然后就可以設置全局異常,例如 當來自后端的請求返回時出現字段error:"GENERIC"。例如:

import React, { Component } from "react"
import axios from "axios"

class GenericErrorReq extends Component {
    constructor(props) {
        super(props)

        this._callBackend = this._callBackend.bind(this)
    }

    render() {
        return (
            
) } _callBackend() { axios .post("/api/city") .then(result => { // do something with it, if the request is successful }) .catch(err => { if (err.response.data.error === "GENERIC") { this.props.setError(err.response.data.description) } }) } } export default GenericErrorReq

如果你很懶,你可以在這里停下來。即使你有具體異常,也可以隨時更改全局異常狀態并在頁面頂部顯示錯誤框。但是,本文展示如何處理和顯示特定的異常。為什么?首先,這是處理異常的指南,所以我不能就此止步。其次,如果你把所有異常都作為全局狀態來顯示,那么UX人員會感到很難受。

處理具體的請求異常

與全局異常類似,我們也可以在其他組件中包含局部異常狀態。 程序是一樣的:

import React, { Component } from "react"
import axios from "axios"

import InlineError from "./InlineError"

class SpecificErrorRequest extends Component {
    constructor(props) {
        super(props)

        this.state = {
            error: "",
        }

        this._callBackend = this._callBackend.bind(this)
    }

    render() {
        return (
            
) } _callBackend() { this.setState({ error: "", }) axios .delete("/api/city") .then(result => { // do something with it, if the request is successful }) .catch(err => { if (err.response.data.error === "GENERIC") { this.props.setError(err.response.data.description) } else { this.setState({ error: err.response.data.description, }) } }) } } export default SpecificErrorRequest

這里要記住的一件事,清除異常通常會有不同的觸發器。 使用"x"刪除異常是沒有意義的。在這里,在發出新請求時清除異常會更有意義。你還可以在用戶進行更改時清除異常,例如,當輸入值改變時。

前端的異常

如前所述,這些異常可以與來自后端的特定異常以相同的方式(狀態)處理。 我這次使用帶有輸入字段的示例,只允許用戶刪除城市,當他實際提供輸入時:

import React, { Component } from "react"
import axios from "axios"

import InlineError from "./InlineError"

class SpecificErrorRequest extends Component {
    constructor(props) {
        super(props)

        this.state = {
            error: "",
            city: "",
        }

        this._callBackend = this._callBackend.bind(this)
        this._changeCity = this._changeCity.bind(this)
    }

    render() {
        return (
            
) } _changeCity(e) { this.setState({ error: "", city: e.target.value, }) } _validate() { if (!this.state.city.length) throw new Error("Please provide a city name.") } _callBackend() { this.setState({ error: "", }) try { this._validate() } catch (err) { return this.setState({ error: err.message }) } axios .delete("/api/city") .then(result => { // do something with it, if the request is successful }) .catch(err => { if (err.response.data.error === "GENERIC") { this.props.setError(err.response.data.description) } else { this.setState({ error: err.response.data.description, }) } }) } } export default SpecificErrorRequest

我希望你對如何處理異常有所了解。忘記console.error(錯誤),它是過去的事情了。 可以使用它進行調試,但它不應該在生產版本中。 為了防止這種情況,我建議你使用一個日志庫,我過去一直在使用loglevel,我很滿意。

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/99378.html

相關文章

  • 作為一個前端工程師也要掌握的幾種文件路徑知識

    摘要:前言之前在做配置時候多次用到路徑相關內容,最近在寫項目的時候,有一個文件需要上傳到阿里云的功能,同時本地服務器也需要保留一個文件備份。如果返回的路徑字符串長度為零,那么他會返回一個,代表當前的文件夾。 showImg(https://segmentfault.com/img/bVbwElJ?w=480&h=204); 前言 之前在做webpack配置時候多次用到路徑相關內容,最近在寫項...

    wslongchen 評論0 收藏0
  • Nginx配置指北之gzip

    摘要:如有錯誤,歡迎指正。如果使用了進行反向代理,那么和后端的之間默認是用協議通信的。如果不是這樣,最好設置為,因為這會造成額外的開銷。一個比較好的處理方式是放在或級別單獨處理。 無論是前端還是后端,在部署項目時,時常免不了用到Nginx,小項目也時常做個反向代理啥的。今天就簡單直接,聊一下其中的一個點——gzip。如有錯誤,歡迎指正。 一般服務器端常用的是 Ubuntu、CentOS、Li...

    yunhao 評論0 收藏0
  • styled-components 中文文檔翻譯及不完全指北

    摘要:前言官方文檔地址中文文檔地址是一個的第三方庫,是的優秀實踐。初次了解是在讀林昊翻譯的設計模式與最佳實踐一書時。能力所限,已翻譯部分可能仍有字詞錯誤或語句不通順的地方,歡迎有能力的同學幫助糾正。就是其中的佼佼者。 前言 官方文檔地址: https://www.styled-components.com/ 中文文檔地址:https://github.com/hengg/styled-com...

    Vicky 評論0 收藏0
  • styled-components 中文文檔翻譯及不完全指北

    摘要:前言官方文檔地址中文文檔地址是一個的第三方庫,是的優秀實踐。初次了解是在讀林昊翻譯的設計模式與最佳實踐一書時。能力所限,已翻譯部分可能仍有字詞錯誤或語句不通順的地方,歡迎有能力的同學幫助糾正。就是其中的佼佼者。 前言 官方文檔地址: https://www.styled-components.com/ 中文文檔地址:https://github.com/hengg/styled-com...

    OnlyLing 評論0 收藏0
  • Node.js 高級進階之 fs 文件模塊學習

    摘要:回調函數提供兩個參數和,表示有沒有錯誤發生,是文件內容。文件關閉第一個參數文件時傳遞的文件描述符第二個參數回調函數回調函數有一個參數錯誤,關閉文件后執行。 showImg(//img.mukewang.com/5d3f890d0001836113660768.jpg); 人所缺乏的不是才干而是志向,不是成功的能力而是勤勞的意志。 —— 部爾衛 文章同步到github博客:https:/...

    verano 評論0 收藏0

發表評論

0條評論

最新活動
閱讀需要支付1元查看
<