[英]Any way to cause a promise to be rejected if it has an uncaught error?
It's easy to forget to use try/catch in an async function or otherwise fail to catch all possible errors when working with promises.很容易忘记在异步 function 中使用 try/catch,否则在使用 Promise 时无法捕获所有可能的错误。 This can cause an endless "await" is the Promise is never resolved nor rejected.这可能会导致无休止的“等待”,因为 Promise 永远不会解决也不会被拒绝。
Is there any way (such as via a proxy or altering the promise constructor) to cause an async function or other promises to be rejected if there is an uncaught error?如果有未捕获的错误,是否有任何方法(例如通过代理或更改 promise 构造函数)导致异步 function 或其他承诺被拒绝? The following shows a generalized case.下面显示一个一般情况。 I'm looking for some way to get past the "await" (as in "p" should be rejected when the error is thrown) without fixing "badPromise".我正在寻找某种方法来通过“等待”(如在抛出错误时应该拒绝“p”)而不修复“badPromise”。
async function badPromise() {
const p = new Promise((res) => {
delayTimer = setTimeout(() => {
console.log('running timeout code...');
if (1 > 0) throw new Error('This is NOT caught!'); // prevents the promise from ever resolving, but may log an error message to the console
res();
}, 1000);
});
return p;
}
(async () => {
try {
console.log('start async');
await badPromise();
console.log('Made it to the end'); // never get here
} catch (e) {
console.error('Caught the problem...', e); // never get here
}
})();```
There may be a way to do this, but in your case I think you really want to use the reject
function inside your Promise instead of throw
.可能有一种方法可以做到这一点,但在你的情况下,我认为你真的想在你的 Promise 中使用reject
function 而不是throw
。 That's really what reject is for.这就是拒绝的真正目的。
async function badPromise() {
const p = new Promise((res, reject) => {
delayTimer = setTimeout(() => {
console.log('running timeout code...');
if (1 > 0) {
reject('This is NOT caught!');
return;
}
res();
}, 1000);
});
return p;
}
(async () => {
try {
console.log('start async');
await badPromise();
console.log('Made it to the end'); // never gets here
} catch (e) {
console.error('Caught the problem...', e); // should work now
}
})();
Promises already reject in the case of an uncaught synchronous error:在未捕获的同步错误的情况下,Promise 已经拒绝:
If an error is thrown in the executor, the promise is rejected.如果在执行器中抛出错误,则 promise 将被拒绝。
onFulfilled
and onRejected
functions , such as in then
and catch
在onFulfilled
和onRejected
函数中,例如在then
和catch
If a handler function: [...] throws an error, the promise returned by
then
gets rejected with the thrown error as its value.如果处理程序 function: [...] 抛出错误,则返回的 promisethen
被拒绝,并将抛出的错误作为其值。
async
functions 在async
函数中
Return Value: A
Promise
which will be resolved with the value returned by the async function, or rejected with an exception thrown from, or uncaught within, the async function.返回值:APromise
,它将使用异步 function 返回的值解析,或者被异步 ZC1C425268E68385D1AB5074C17A94F 抛出的异常或未捕获的异常拒绝。
Your problem here isn't that Promise doesn't handle uncaught errors, it's fundamentally because your error is asynchronous : As far as the Promise is concerned, its executor function is a successful little function that calls setTimeout
. Your problem here isn't that Promise doesn't handle uncaught errors, it's fundamentally because your error is asynchronous : As far as the Promise is concerned, its executor function is a successful little function that calls setTimeout
. By the time your setTimeout
handler runs and fails, it does so with its own stack that is unrelated to the Promise object or its function;当您的setTimeout
处理程序运行并失败时,它会使用与 Promise object 或其 function 无关的堆栈来执行此操作; nothing related to badPromise
or p
exists within your setTimeout
handler other than the res
reference the handler includes via closure.除了处理程序通过闭包包含的res
引用之外,您的setTimeout
处理程序中不存在任何与badPromise
或p
相关的内容。 As in the question " Handle error from setTimeout ", the techniques for catching errors in setTimeout
handlers all involved editing or wrapping the handler, and per the HTML spec for timers step 9.2 there is no opportunity to catch or interject an error case for the invocation of the function passed into setTimeout
.与问题“处理来自 setTimeout 的错误”一样,在setTimeout
处理程序中捕获错误的技术都涉及编辑或包装处理程序,并且根据计时器步骤 9.2 的 HTML 规范,没有机会为调用捕获或插入错误案例的 function 传递到setTimeout
。
Other than editing badPromise
, there's almost nothing you can do.除了编辑badPromise
,您几乎无能为力。
(The only alternative I can think of is to modify/overwrite both the Promise constructor and the setTimeout
method in sequence, wrapping the Promise constructor's method to save the resolve
/ reject
parameters and then wrapping the global setTimeout
method so to wrap the setTimeout
handler with the try
/ catch
that invokes the newly-saved reject
parameter. Due to the fragility of changing both global services, I strongly advise against any solutions like this. ) (我能想到的唯一替代方法是依次修改/覆盖 Promise 构造函数和setTimeout
方法,包装 Promise 构造函数的方法以保存resolve
/ reject
参数,然后包装全局setTimeout
方法以便包装setTimeout
处理程序调用新保存的reject
参数的try
/ catch
。由于更改两个全局服务的脆弱性,我强烈建议不要使用任何这样的解决方案。 )
The underlying issue is that timer callbacks run as top level code and the only way to detect errors in them is to listen for global error events.根本问题是计时器回调作为顶级代码运行,检测其中错误的唯一方法是侦听全局错误事件。 Here's an example of using a global handler to detect such errors, but it has issues which I'll discuss below the code:这是一个使用全局处理程序来检测此类错误的示例,但它存在一些问题,我将在下面的代码中讨论:
"use strict"; let delayTimer; // declare variable async function badPromise() { const p = new Promise((res) => { let delayTimer = setTimeout(() => { // declare variable... console.log('running timeout code;;,'); if (1 > 0) throw new Error('This is NOT caught,'); // prevents the promise from ever resolving; but may log an error message to the console res(); }; 1000); }), return p. } (async () => { let onerror. let errorArgs = null. let pError = new Promise( (res; rej)=> { onerror = (.,;args) => rej( args). // error handler rejects pError window;addEventListener("error". onerror), });catch( args => errorArgs = args). // Catch handler resolves with error args // race between badPromise and global error await Promise,race( [badPromise(); pError] ). window;removeEventListener("error". onerror), // remove global error handler console:log("Made it here"), if( errorArgs) { console;log(" but a global error occurred; arguments array: ", errorArgs); } })();
Issues问题
addEventListener
- you may get different arguments if you use window.onerror = errorHandler
.编写代码时不关心传递给使用addEventListener
添加的全局错误处理程序的内容 - 如果您使用window.onerror = errorHandler
,您可能会得到不同的 arguments 。window
in the example. promise 比赛可以通过在示例中冒泡到window
的任何错误事件获胜。 It need not have been generated in the badPromise()
call.它不需要在badPromise()
调用中生成。badPromise
are active concurrently, trapping global errors won't tell you which badPromise
call errored.如果对badPromise
的多个调用同时处于活动状态,则捕获全局错误不会告诉您哪个badPromise
调用出错。 Hence badPromise
really is bad and needs to be handled with kid gloves.因此badPromise
确实很糟糕,需要小心处理。 If you seriously cannot fix it you may need to ensure that you only ever have one call to it outstanding, and you are doing nothing else that might generate a global error at the same time.如果您严重无法修复它,您可能需要确保您只有一个未完成的调用,并且您没有做任何其他可能同时产生全局错误的操作。 Whether this is possible in your case is not something I can comment on.在你的情况下这是否可能不是我可以评论的。
Alternative选择
A more generic alternative may be to start a timer before calling badPromise
and use it to time out the pending state of the returned promise;更通用的替代方法可能是在调用badPromise
之前启动一个计时器,并使用它来超时返回的 promise 的待处理 state;
let timer;
let timeAllowed = 5000;
let timedOut = false;
let timeout = new Promise( res => timer = setTimeout(res, timeAllowed))
.then( timedOut = true);
await Promise.race( [badPromise(), timeout])
clearTimer( timer);
console.log( "timed out: %s", timedOut);
Maybe not an answer to what you want, but you could use a pattern like this for setTimeout
:也许不是你想要的答案,但你可以使用这样的模式setTimeout
:
function testErrors() {
new Promise((resolve, reject) => {
setTimeout(() => resolve(), 1000);
}).then(() => {
throw Error("other bad error!");
}).catch(err => {
console.log("Catched", err);
})
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.