中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

JavsScript中Promise的錯誤捕獲問題怎么解決

發布時間:2022-06-28 14:14:04 來源:億速云 閱讀:193 作者:iii 欄目:開發技術

本篇內容介紹了“JavsScript中Promise的錯誤捕獲問題怎么解決”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!

我們需要在異步任務中準確的進行錯誤捕獲,以便我們可以知道錯誤出在什么地方

如果對Promise和trycatch不夠理解的話,很多時候會出現Promise中的錯誤無法被捕獲的情況,本文來討論這些情況

try catch

try catch 只能捕獲當前上下文中的錯誤,也就是只能捕獲同步任務的情況,如下場景:

try {
    throw "程序執行遇到了一些錯誤";
} catch(e) {
    console.log(e)
}
// 控制臺會輸出:程序執行遇到了一些錯誤

這很好,錯誤被捕獲了,我們可以在程序中進程錯誤的處理;

但是對于異步的任務,trycatch就顯得無能為力,不能正確捕獲錯誤:

try {
    setTimeout(() => {
      throw "程序執行遇到了一些錯誤"  
    })
} catch(e) {
    console.log(e);
}
// 控制臺輸出:Uncaught 程序執行遇到了一些錯誤;

又或者這樣:

try {
    Promise.reject('程序執行遇到了一些錯誤');
} catch(e) {
    console.log(e);
}
// 控制臺輸出:Uncaught (in promise) 程序執行遇到了一些錯誤

上面的代碼都無法正常捕獲到錯誤,因為:trycatch永遠捕獲的是同步的錯誤

什么是同步的錯誤?

當在一個事件循環內,同一個任務隊列中出現的錯誤,對于這個任務所在的上下文而言,就是同步錯誤。

setTimeoutPromise被稱為任務源,來自不同的任務源注冊的回調函數會被放入不同的任務隊列中。

  • setTimeout中的任務會被放入宏任務

  • Promise中的任務會被放入微任務

    • 拓展:setTimeout是宿主瀏覽器發起的任務,一般會被放入宏任務

    • 而Promise是由JS引擎發起的任務,會被放入微任務

第一次事件循環中,JS引擎會把整個script代碼當成一個宏任務執行,執行完成之后,再檢測本次循環中是否存在微任務,存在的話就依次從微任務的任務隊列中讀取執行完所有的微任務,再讀取宏任務的任務隊列中的任務執行,再執行所有的微任務,如此循環。

JS的執行順序就是每次事件循環中的宏任務-微任務的不斷切換。

再看setTimeout中拋出的錯誤,這個錯誤已經不在trycatch所在的事件循環中了,所以這是一個異步錯誤,無法被trycatch捕獲到。

同理,Promise.reject()此處雖然是同步執行的,但是此處reject的內容卻在另一個微任務循環中,對于trycatch來講也不是同步的,所以這兩個錯誤都無法被捕獲。

Promise.reject

要理解Promise.reject首先要了解它的返回值,Promise.reject返回的是一個Promise對象,請注意:是Promise對象

Promise對象在任何時候都是一個合法的對象,它不是錯誤也不是異常,所以在任何實現,直接對Promise.reject或者一個返回Promise對象的調用直接try catch是沒有意義的,一個正常的對象永遠不可能觸發catch捕獲。

假設我們由如下代碼:

function getData() {
    Promise.reject('遇到了一些錯誤');
};
function click() {
    try {
        getData();
    } catch(e) {
        console.log(e);
    }
}
click(); // 我們模擬業務場景中的click事件
// 控制臺輸出: Uncaught (in promise) 遇到了一些錯誤

Promise已經通過reject拋出了錯誤,為什么try catch捕獲不到呢?

首先,需要知道,對于一個函數的錯誤是否可以被捕獲到,可以嘗試將函數調用的返回值替換到函數調用出,看看是否為一個錯誤

上面getDate()調用會被替換為undefined

對于一個沒有明確return的函數調用,其返回值永遠是undefined的,所以代碼如下:

function click() {
    try {
        undefined;
    } catch(e) {
        console.log(e);
    }
}

了解js基礎的人肯定知道,這不算異常,這個代碼會正常執行,不會走到catch中。

可能會有另一種思路,就是將Promise.reject返回出去,那么代碼就變成:

function getData() {
  return Promise.reject('遇到了一些錯誤');
};
function click() {
    try {
        getData();
    } catch(e) {
        console.log(e);
    }
}
click();

Promise.reject返回的是一個Promise對象,它是對象,不是錯誤。所以在try catch中完成getData()調用后這里會出現一個Promise對象,這個對象是一個再正常不過的對象,不會被catch捕獲,所以這個try catch依然是無效的。

于是,又出現一種思路:再調用處使用Promise的catch方法進行捕獲,于是代碼變成:

function getData() {
  return Promise.reject('遇到了一些錯誤');
};
function click() {
    try {
        getData().catch(console.log);
    } catch(e) {
        console.log(e);
    }
}
click();

這是可行的,rejext的錯誤可以被捕獲,但這不是try catch的功勞,而是Promise的內部消化,所以這里的try catch依然沒有意義。

解決Promise異常捕獲

Promise異常是最常見的異步異常,其內部的錯誤基本都是被包裝成了Promise對象后進行傳遞,所以解決Promise異步捕獲整體思路有兩個:

  • 使用Promise的catch方法內部消化;

  • 使用async和await將異步錯誤轉同步錯誤再由try catch捕獲

Promise.catch

對于Promise.reject中拋出的錯誤,或者Promise構造器中拋出的錯誤,亦或者then中出現的錯誤,無論是運行時還是通過throw主動拋出的,原則上都可以被catch捕獲。

如下:

function getData() {
    Promise.reject('這里發生了錯誤').catch(console.log);
}

function click() {
    getData();
}

click();

亦或者在調用處捕獲,但這需要被調用的函數能返回Promise對象;

function getData() {
    return Promise.reject('程序發生了一些錯誤');
}

function click() {
    getData().catch(console.log);
}
click();

上面兩個方案都可行,事實上建議在業務邏輯允許的情況下,將Promise都返回出去,以便能向上傳遞,同時配合**unhandledrejection**進行兜底

async await 異步轉同步

使用async和await可以將一個異步函數調用在語義上變成同步執行的效果,這樣我們就可以使用try catch去統一處理。

例如:

function getData() {
    return Promise.reject('程序發生錯誤');
}
async function click() {
    try {
        await getData();
    } catch(e) {
        console.log(e);
    }
}

click();

需要注意的是,如果getData方法沒有寫return, 那么就無法將Promise對象向上傳遞,那么調用出的await等到的就是一個展開的undefined, 依舊不能進行錯誤處理。

注意事項

一個函數如果內部處理了Promise異步對象,那么原則上其處理結果應該也是一個Promise對象,對于需要進行錯誤捕獲的場景,Promise對象應該始終通過return向上傳遞

兜底方案

一般情況下,同步錯誤如果沒有進行捕獲,那么這個錯誤所在的事件循環將終止,所以在開發階段沒有捕獲的錯誤,使用一種方法進行兜底是很有必要的。

對于同步錯誤,可以定義window.onerror進行兜底處理,或者使用window.addEventListener('error', errHandler)來定義兜底函數。

對于Promise異常,則可以同步使用window.onunhandledrejection或者window.addEventListener('unhandledrejection', errHandler)來定義兜底函數。

我們再討論then方法中的第二個參數和Promise.catch方法的區別

Promise中的then的第二個參數和catch有什么區別?

  • reject是用來拋出錯誤的,屬于Promise的方法

  • catch是用來處理異常的,屬于Promise實例的方法、

  • 區別

    主要區別就是,如果在then的第一個函數中拋出了異常,后面的catch能捕獲到,但是then的第二個參數卻捕獲不到

    then的第二個參數和catch捕獲信息的時候會遵循就近原則,如果是promise內部報錯,reject拋出錯誤后,then的第二個參數和catch方法都存在的情況下,只有then的第二個參數能捕獲到,如果then的第二個參數不存在,則catch方法會被捕獲到。

題: then方法的連續調用,怎么能夠知道是第幾個then方法報錯了呢。

new Promise((resolve,reject) => {
    setTimeout(() => {
        resolve(1);
    }, 1000)
}).then(res => {
    console.log(res);
    return new Promise((resolve,reject) => {
        reject('第一個then方法報錯了');
    })
}).then(res => {
    console.log(res);
    return new Promise((resolve,reject) => {
		reject('第二個then方法報錯了');
    })
}).catch(err => {
    console.log(err);
})

“JavsScript中Promise的錯誤捕獲問題怎么解決”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

抚顺县| 晋中市| 大姚县| 札达县| 万安县| 南乐县| 克山县| 溧水县| 都安| 普安县| 比如县| 龙门县| 平和县| 阿鲁科尔沁旗| 义乌市| 德钦县| 巴彦县| 云林县| 上杭县| 云龙县| 专栏| 东明县| 沙雅县| 青岛市| 长武县| 延庆县| 托克托县| 蒙自县| 特克斯县| 克什克腾旗| 含山县| 滨州市| 新干县| 舞钢市| 丽水市| 鸡东县| 湟源县| 三台县| 三门峡市| 泾阳县| 武隆县|