JavaScript中Promise已加Catch仍触发Uncaught错误的问题
Promise错误无法被外层try/catch捕获的问题分析与解决
问题场景
在JavaScript中调用外部API时,尝试用外层try/catch捕获Promise异步操作的错误,但控制台仍抛出Uncaught (in promise) Error: Error,无法触发预期的doSomethingElse()。复现代码如下:
外层调用代码:
try { functionGeneratingTheError(); } catch (error) { doSomethingElse(); }
异步请求函数:
function functionGeneratingTheError() { var getTokenCallPayload = { "client_id" : clientId, "client_secret" : clientSecret, "refresh_token" : refreshToken, "grant_type" : "refresh_token" }; var getTokenCallOptions = { "method" : "POST", "body" : JSON.stringify(getTokenCallPayload), "muteHttpExceptions" : false }; fetch(tokenURL, getTokenCallOptions) .then(response => { if (response.ok) { return response.json(); } else { throw new Error("Error"); } }) .then(data => { doSomething(); }) .then(response=> { doSomethingAgain(); }) .catch(error => { throw error; }); }
问题原因
- 同步try/catch无法捕获异步错误:外层
try/catch是同步代码,而fetch是异步操作,异步代码的错误会在事件循环的后续阶段抛出,此时同步的try/catch已经执行完毕,无法捕获该错误。 - 内部catch重新抛出未处理的错误:Promise链中的
catch虽然捕获了错误,但又通过throw error重新抛出,且没有后续代码处理这个抛出的错误,导致浏览器提示未捕获的Promise错误。 - 直接调用doSomethingElse()会触发后续then报错:如果在内部catch直接调用
doSomethingElse(),Promise链不会中断,后续的.then会继续执行,因缺少data参数而报错。
解决方案
方案一:使用async/await配合外层try/catch
让异步函数返回Promise,在外层用async/await将异步操作转为同步写法,这样try/catch就能捕获异步错误。
修改后代码:
// 外层处理函数改为async函数 async function handleFormSubmit() { try { await functionGeneratingTheError(); } catch (error) { doSomethingElse(); } } function functionGeneratingTheError() { var getTokenCallPayload = { "client_id" : clientId, "client_secret" : clientSecret, "refresh_token" : refreshToken, "grant_type" : "refresh_token" }; var getTokenCallOptions = { "method" : "POST", "body" : JSON.stringify(getTokenCallPayload), "muteHttpExceptions" : false }; // 返回Promise链,让外层await能捕获错误 return fetch(tokenURL, getTokenCallOptions) .then(response => { if (response.ok) { return response.json(); } else { throw new Error("Error"); } }) .then(data => { doSomething(); }) .then(response=> { doSomethingAgain(); }); }
方案二:在内部Promise链的catch中直接处理错误
去掉内部catch的throw error,直接在catch中执行doSomethingElse(),同时如果需要中断后续链式调用,可以返回Promise.reject()(若有后续链),或直接终止:
function functionGeneratingTheError() { var getTokenCallPayload = { "client_id" : clientId, "client_secret" : clientSecret, "refresh_token" : refreshToken, "grant_type" : "refresh_token" }; var getTokenCallOptions = { "method" : "POST", "body" : JSON.stringify(getTokenCallPayload), "muteHttpExceptions" : false }; fetch(tokenURL, getTokenCallOptions) .then(response => { if (response.ok) { return response.json(); } else { throw new Error("Error"); } }) .then(data => { doSomething(); }) .then(response=> { doSomethingAgain(); }) .catch(error => { // 直接处理错误,不再抛出 doSomethingElse(); // 若需明确中断后续可能的链式调用,可返回rejected Promise // return Promise.reject(error); }); }
内容的提问来源于stack exchange,提问作者Quentin Escaron
相关产品推荐
相关产品推荐

