简体   繁体   English

如果 promise 有未捕获的错误,有什么方法可以导致它被拒绝?

[英]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 已经拒绝:

  • in a Promise constructor , for synchronous (thrown) errors 在 Promise 构造函数中,用于同步(抛出)错误

    If an error is thrown in the executor, the promise is rejected.如果在执行器中抛出错误,则 promise 将被拒绝。

  • in onFulfilled and onRejected functions , such as in then and catch onFulfilledonRejected函数中,例如在thencatch

    If a handler function: [...] throws an error, the promise returned by then gets rejected with the thrown error as its value.如果处理程序 function: [...] 抛出错误,则返回的 promise then被拒绝,并将抛出的错误作为其值。

  • in 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.返回值:A Promise ,它将使用异步 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处理程序中不存在任何与badPromisep相关的内容。 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问题

  • The code was written without caring what is passed to an global error handler added using addEventListener - you may get different arguments if you use window.onerror = errorHandler .编写代码时不关心传递给使用addEventListener添加的全局错误处理程序的内容 - 如果您使用window.onerror = errorHandler ,您可能会得到不同的 arguments 。
  • The promise race can be won by any error event that bubbles up to window in the example. promise 比赛可以通过在示例中冒泡到window的任何错误事件获胜。 It need not have been generated in the badPromise() call.它不需要在badPromise()调用中生成。
  • If multiple calls to 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.

相关问题 有没有办法知道用户是否拒绝了小程序? - Is there any way to know if the user has rejected the applet? 未捕获(承诺):错误:无法匹配任何路由:“” - Uncaught (in promise): Error: Cannot match any routes: ' ' 捕获拒绝承诺并用错误代码将其转换为解决任何问题? - Any problem with catch rejected promise and turn it into resolved with error code? 什么会导致此处因“ InvalidStateError”而被拒绝的承诺? - What can cause a Promise rejected with 'InvalidStateError' here? 有没有办法检测被拒绝的承诺是否未处理? - Is there a way detect if a rejected promise is unhandled? EXCEPTION:错误:未捕获(在承诺中):错误:无法匹配任何路由:''“ - EXCEPTION: Error: Uncaught (in promise): Error: Cannot match any routes: ''" 未捕获(承诺):错误:无法匹配任何路线(Angular8) - Uncaught (in promise): Error: Cannot match any routes (Angular8) 未捕获(承诺中)错误:第 1 行有 42 列,但必须有 2 - Uncaught (in promise) Error: Row 1 has 42 columns, but must have 2 未捕获(承诺):服务器错误 - Uncaught (in promise): Server error Promise 中的 ComponentDidMount 未捕获错误 - ComponentDidMount Uncaught Error in Promise
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM