用一级 try ... catch 捕获 JavaScript Promise 中的错误
Posted
技术标签:
【中文标题】用一级 try ... catch 捕获 JavaScript Promise 中的错误【英文标题】:Catching Errors in JavaScript Promises with a First Level try ... catch 【发布时间】:2014-09-18 14:15:39 【问题描述】:所以,我希望我的第一级捕获是处理错误的那个。有没有办法将我的错误传播到第一次捕获?
参考代码,不工作(还):
Promise = require('./framework/libraries/bluebird.js');
function promise()
var promise = new Promise(function(resolve, reject)
throw('Oh no!');
);
promise.catch(function(error)
throw(error);
);
try
promise();
// I WANT THIS CATCH TO CATCH THE ERROR THROWN IN THE PROMISE
catch(error)
console.log('Caught!', error);
【问题讨论】:
只是让你知道,你要求混淆有两个不同的东西命名为promise
(一个函数和一个变量),更不用说内置的Promise
。我相信你可以让它工作,但为什么要让你的代码那样混乱呢?
【参考方案1】:
您不能使用 try-catch 语句来处理异步抛出的异常,因为该函数在抛出任何异常之前已经“返回”。您应该改用promise.then
和promise.catch
方法,它们代表try-catch 语句的异步等效项。 (或者使用@Edo 的回答中提到的 async/await 语法。)
您需要做的是返回承诺,然后将另一个 .catch
链接到它:
function promise()
var promise = new Promise(function(resolve, reject)
throw('Oh no!');
);
return promise.catch(function(error)
throw(error);
);
promise().catch(function(error)
console.log('Caught!', error);
);
Promise 是可链接的,因此如果一个 Promise 重新抛出错误,它将被委托给下一个 .catch
。
顺便说一句,您不需要在throw
语句周围使用括号(throw a
与throw(a)
相同)。
【讨论】:
@Kirk:如果你不能编辑这个promise
函数,我只会这样做,因为一次使用一个函数会更加一致。在这种情况下,我编辑了我的答案以添加指向可能解决方案的链接。
对于它的价值,你绝对应该不在我的回答中使用该方法,而应该使用 Promise。 Promise 已经解决了这个问题并提供了投掷安全性。你可以简单地省略promise.catch
,事实上,上面的整个代码都可以重构为Promise.reject("oh no")
(尽管你应该总是有错误拒绝)。然后你可以做Promise.reject("oh no").catch(function(e) console.log(e); );
记录“哦不”。实际上,除了可链接性和可组合性之外,Throw 安全性是 Promise 的最大卖点之一。
@BenjaminGruenbaum:我假设这只是一个基于其他代码的模型示例,有时(重新)抛出错误,而不是确切的代码。无论如何,我已经编辑了我的答案,以更加强调使用承诺。【参考方案2】:
使用新的async/await syntax,您可以实现这一目标。请注意,在撰写本文时,并非所有浏览器都支持此功能,您可能需要使用 babel(或类似名称)转译您的代码。
// Because of the "async" keyword here, calling getSomeValue()
// will return a promise.
async function getSomeValue()
if (somethingIsNotOk)
throw new Error('uh oh');
else
return 'Yay!';
async function()
try
// "await" will wait for the promise to resolve or reject
// if it rejects, an error will be thrown, which you can
// catch with a regular try/catch block
const someValue = await getSomeValue();
doSomethingWith(someValue);
catch (error)
console.error(error);
【讨论】:
如果函数返回 Promise,我们是否需要将该方法声明为异步。 如果你想在该函数中使用await
,你只需要 async 关键字。使用 async 关键字将产生一个副作用,即承诺将返回一个承诺。如果你想等待一个函数 X 的结果,它返回一个 promise 开始,使函数 X 异步将没有效果。
缺失:声明 Promise.reject(err);
内部 catch ***.com/a/37993829/4933053
@ItsmeJulian 这取决于你想如何处理它。如果您只想在 catch 中登录,那么您不需要将错误冒泡。另外,如果你想把它冒泡,我想我更喜欢throw err
inside catch。【参考方案3】:
不!这完全不可能,因为 Promise 本质上是异步的。当抛出异常时,try-catch 子句将完成执行(并且还没有发明时间旅行)。
相反,从所有函数返回 Promise,并在它们上挂钩错误处理程序。
【讨论】:
【参考方案4】:我经常发现需要确保返回 Promise,并且几乎同样需要处理本地错误,然后选择性地重新抛出它。
function doSomeWork()
return Promise.try(function()
return request.get(url).then(function(response)
// ... do some specific work
);
).catch(function(err)
console.log("Some specific work failed", err);
throw err; // IMPORTANT! throw unless you intend to suppress the error
);
这种技术 (Promise.try/catch) 的好处是,您可以在没有解决/拒绝要求的情况下启动/确保 Promise 链,这很容易被忽略并造成调试噩梦。 p>
【讨论】:
【参考方案5】:要扩展edo 的答案,如果您想捕获不想等待的异步函数的错误。您可以在函数末尾添加等待语句。
(async function()
try
const asyncResult = someAsyncAction();
// "await" will wait for the promise to resolve or reject
// if it rejects, an error will be thrown, which you can
// catch with a regular try/catch block
const someValue = await getSomeValue();
doSomethingWith(someValue);
await asyncResult;
catch (error)
console.error(error);
)();
如果someAsyncAction
失败,catch 语句将处理它。
【讨论】:
请注意:这仍然会停止外部函数的完成,直到 asyncResult 完成(出现错误或成功)。如果您根本不想等待 asyncResult,则在someAsyncAction()
上链接 .catch
可能更容易。
我不确定你说它会停止外部函数的完成是什么意思,但它不会阻塞事件循环。正如它所写的那样,任何放置在外部函数之后的代码的执行都不会被函数内部的 Promise 解析所阻塞。通过将.catch
添加到someAsyncAction()
,您将获得的唯一好处是错误处理将被同步绑定。如果someAsyncAction
在到达await asyncResult
行之前被拒绝,这将避免无关的UnhandledPromiseRejectionWarning
消息。
我的意思是,如果某些代码是 await
-ing 外部函数,那么该代码仍然需要等待 someAsyncAction
才能完成。很多时候,人们可能想要触发一些异步操作,但不等待解决/错误的承诺,例如发送分析数据。现在,当链接.catch
(并完全省略await asyncResult
)时,您仍然可以处理someAsyncAction
上的异步错误,同时确保没有其他逻辑在等待它。以上是关于用一级 try ... catch 捕获 JavaScript Promise 中的错误的主要内容,如果未能解决你的问题,请参考以下文章
php中,用try/catch捕获了异常,为啥还会有警告?有没有办法去掉呢?
测试 __try, __finally, __except(被__finally捕获的异常, 还会被上一级的__except捕获。反之不行)