简体   繁体   English

为什么我的ES6 Promise Rejection需要立即使用以避免出现控制台错误消息?

[英]Why does my ES6 Promise Rejection need to be consumed immediately to avoid a console error message?

Please note: The following is an issue that behaves differently on different browsers. 请注意:以下是在不同浏览器上表现不同的问题。 So perhaps this is a browser implementation issue. 所以这可能是一个浏览器实现问题。 I would love some advice regardless. 无论如何,我会喜欢一些建议。

In my application, I am creating a couple of promises that I may not be consuming until quite some time in the future. 在我的应用程序中,我创建了一些承诺,我可能不会消费,直到将来很长一段时间。 Which should be fine, they are promises, after all. 哪个应该没问题,毕竟它们是承诺。

If the promise being stored away is resolved, there is no issue. 如果解决了存储的承诺,则没有问题。 I can consume it as far in the future as I want, and as many times as I want. 我可以根据自己的需要尽可能多地消费它,也可以随心所欲地消费它。 As expected. 正如所料。

If the promise being stored away is rejected, however, there is an issue. 但是,如果被拒绝的承诺被拒绝,则存在问题。 Unless I consume that rejection shortly after it is made (not sure how shortly) a console message will crop up in Chrome or Firefox indicating that there is an uncaught promise rejection/error. 除非我在制作完成后立即消除拒绝(不确定多久),否则Chrome或Firefox中会出现一个控制台消息,表明存在未被拒绝的承诺拒绝/错误。 IE does not pop up that error. IE不会弹出该错误。

So consider the following code: 请考虑以下代码:

console.log("About to make a promise rejection.");
var foo = Promise.reject(new Error("Foo Rejected Promise."));
console.log("Promise rejection made.");

Note that there is no use or consumption of the promise foo. 请注意,没有使用或消费promise foo。 It is merely stored away for some future use. 它只是存储起来以备将来使用。

The console on IE looks like this: IE上的控制台如下所示:

About to make a promise rejection. 即将拒绝承诺。

Promise rejection made. 承诺拒绝。

Which is expected. 这是预期的。 However, the same code on Chrome will yield the following console: 但是,Chrome上的相同代码将生成以下控制台:

About to make a promise rejection. 即将拒绝承诺。

Promise rejection made. 承诺拒绝。

Uncaught (in promise) Error: Foo Rejected Promise.(…) 未捕(承诺)错误:Foo拒绝承诺。(...)

Firefox looks pretty much like Chrome, other than the wording around the "uncaught" error. 除了围绕“未捕获”错误的措辞之外,Firefox看起来非常像Chrome。

But the thing is that I intend to handle this "error" much later, at the time I am consuming the promise. 但问题是,我打算在很久以后处理这个“错误”,当时我正在消费这个承诺。 Merely HAVING a rejected promise should not cause a console error..... that should happen if I consume the promise and don't handle the error. 仅仅有一个被拒绝的承诺不应该导致控制台错误.....如果我使用了承诺并且没有处理错误就应该发生。

To simulate a "much later" handling of the error, consider this alteration of the code: 要模拟错误的“更晚”处理,请考虑对代码的这种更改:

console.log("About to make a promise rejection.");
var foo = Promise.reject(new Error("Foo Rejected Promise."));
console.log("Promise rejection made.");
setTimeout(() => {
    foo.catch((err) => {
        console.log("Displaying " + err.message + " 10 seconds later.");
    });
}, 10000);

Now in this case, we "handle" the error by displaying something on the console after a timeout. 现在在这种情况下,我们通过在超时后在控制台上显示某些内容来“处理”错误。 Now IE still handles this as I would expect: 现在IE仍然按照我的预期处理这个:

About to make a promise rejection. 即将拒绝承诺。

Promise rejection made. 承诺拒绝。

Displaying Foo Rejected Promise. 显示Foo拒绝的承诺。 10 seconds later. 10秒后

In this case, Firefox does like IE, and displays exactly these messages, and does not give me an erroneous console error. 在这种情况下,Firefox确实喜欢IE,并准确显示这些消息,并没有给我一个错误的控制台错误。

Chrome, however, still gives the erroneous error: 但是,Chrome仍会出现错误错误:

About to make a promise rejection. 即将拒绝承诺。

Promise rejection made. 承诺拒绝。

Uncaught (in promise) Error: Foo Rejected Promise.(…) 未捕(承诺)错误:Foo拒绝承诺。(...)

Displaying Foo Rejected Promise. 显示Foo拒绝的承诺。 10 seconds later. 10秒后

So Chrome both complained about my error not being handled, and then displayed that it was handled. 所以Chrome都抱怨我的错误没有被处理,然后显示它已被处理。

It appears that I can get around all this with the following code that that seems like a hack. 似乎我可以使用以下代码来解决这一切,这看起来像是一个黑客。 Basically I do a "fake" handling of the error when the promise is created and then really handle it the way I want to later: 基本上我在创建promise时对错误进行“假”处理,然后以我想要的方式真正处理它:

console.log("About to make a promise rejection.");
var foo = Promise.reject(new Error("Foo Rejected Promise."));
foo.catch(() => { });  // Added this hack-ish code.
console.log("Promise rejection made.");
    setTimeout(() => {
    foo.catch((err) => {
        console.log("Displaying " + err.message + " 10 seconds later.");
    });
}, 10000);

But this is ugly code. 但这是丑陋的代码。

My question is twofold - is there some way of looking at what Chrome (and to some extent FireFox) is doing and think of it as a favor? 我的问题是双重的 - 有什么方法可以看看Chrome(以及某种程度上FireFox)正在做什么并将其视为一种恩惠? Because to me it seems awful. 因为对我而言似乎很糟糕。 And secondly, is there a better way of getting around this than the hack of pretending to handle an error when you aren't? 其次,当你不是假装处理错误的黑客时,是否有更好的解决方法呢?

Thanks in advance. 提前致谢。

As someone who has worked with promises for a couple of years now, I understand why this behavior is set in Chrome. 作为已经使用承诺工作了几年的人,我理解为什么在Chrome中设置此行为。 it is easy to forget to catch a rejected promise, and if there is an error in .then(), there will be no notification and your program will just stay silent blankly. 很容易忘记捕获被拒绝的承诺,如果.then()中有错误,则没有通知,您的程序将只是保持沉默。 It can be very hard to debug. 调试起来非常困难。

function throwException(c){ return a.b = 2 ;} //a is not defined. throws exception.
let p = q.resolve("a")
         .then(x => x + "b")
         .then(y => throwException(y))
         .then(z=> z+"c");   //this will say nothing in Q library 

this can easily create a very obscure behaviour when one forgets to catch the error. 当忘记捕获错误时,这很容易创建一个非常模糊的行为。

In Q library, you have the .done() function to actually enforce what Chrome is doing here: 在Q库中,您使用.done()函数来实际执行Chrome在此处执行的操作:

let p = q.resolve("a")
         .then(x => x + "b")
         .then(y => throwException(y))
         .then(z=> z+"c")
         .done();   //this will throw an exception.

So, it is a known problem in promises. 所以,这是承诺中的一个已知问题。 Chrome have simply decided to make .done() the default behavior, asking you to explicitly .catch(_=>{}) if you want to ignore failed promises. Chrome只是决定使.done()成为默认行为,如果你想忽略失败的承诺,则要求你明确.catch(_=>{}) Q has decided to make silent failure default, asking you to explicitly use .done() if you want an exception on failed promise. Q已决定使默认失败默认,如果您希望失败的承诺出现异常,则要求您明确使用.done() If you like the latter approach, you can always include a promise library. 如果您喜欢后一种方法,则可以随时包含一个promise库。 It is a matter of taste IMHO. 这是一个品味恕我直言。

Any promise that has a chance to be rejected, should be handled, similarly to exceptions. 任何有机会被拒绝的承诺都应该被处理,类似于例外情况。

There are not so many chances that a promise won't be chained synchronously. 承诺不会同步链接的可能性不大。 But if it won't, it requires 'hack-ish' .catch(() => { }) to synchronously handle the rejection. 但如果不这样做,则需要'hack- .catch(() => { })来同步处理拒绝。

Chrome promise rejection behaviour puts this Chrome承诺拒绝行为就是这样

var foo = Promise.reject(new Error("Foo Rejected Promise."));
foo.catch(() => { });  // Added this hack-ish code.

setTimeout(() => {
    foo.catch((err) => {
        console.log("Displaying " + err.message + " 10 seconds later.");
    });
}, 10000);

into the same error-handling boat as 进入同样的错误处理船

try {
  throw new Error("Foo"));

  setTimeout(() => {
    try {
      throw new Error("Foo"));
    } catch (err) {
      console.log("Displaying " + err.message + " 10 seconds later.");
    }
  }, 10000);
} catch (err) {}

Wouldn't the exception wait 10 seconds to be caught by next try...catch before being thrown? 异常不会等待10秒被下一次try...catch之前被抛出? It won't wait even a single tick. 它不会等到一个滴答。 It requires 'hack-ish' try { ... } catch (err) {} block to perform as intended. 它需要'hack-ish'try try { ... } catch (err) {}块来按预期执行。

This behaviour is already well-known to Bluebird library users for quite a long time. 很长一段时间,Bluebird库用户已经熟知这种行为。 Bluebird has adjustable error handling and allows to debug promises efficiently even on the large scale. Bluebird具有可调节的错误处理功能,即使在大规模上也可以高效地调试承诺

The behaviour is also known in Angular 2 development, it is forced for promises with Zone.js library. 这种行为在Angular 2开发中也是已知的,它被强制用于Zone.js库的promises。

Since promise debugging has limited value and applicable to development environment, it is possible to modify or disable this behaviour for native promises in production builds: 由于promise调试的价值有限且适用于开发环境,因此可以在生产版本中修改或禁用本机承诺的此行为:

if (typeof DEBUG_PROMISE === 'undefined') {
  window.addEventListener('unhandledrejection', (e) => {
    e.preventDefault();
    console.warn(e.reason);
  });
}

Here's more reading on the subject. 这里有关于这个主题的更多阅读

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM