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

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

请注意:以下是在不同浏览器中表现不同的问题。所以也许这是一个浏览器实现问题。不管怎样,我都会喜欢一些建议。

在我的应用程序中,我创建了几个承诺,我可能要在未来相当长的一段时间内才会使用这些承诺。这应该没问题,毕竟是承诺。

如果存储的 promise 已解决,则没有问题。我可以在未来尽可能多地使用它,并且可以多次使用它。不出所料。

但是,如果被存储的承诺被拒绝,那就有问题了。除非我在做出拒绝后不久(不确定多快)消费该拒绝,否则控制台消息将在 Chrome 或 Firefox 中弹出,表明存在未捕获的承诺 rejection/error。 IE不弹出那个错误。

所以考虑下面的代码:

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

请注意,没有使用或消耗 promise foo。它只是存放起来以备将来使用。

IE 上的控制台如下所示:

About to make a promise rejection.

Promise rejection made.

这是意料之中的。但是,Chrome 上的相同代码将产生以下控制台:

About to make a promise rejection.

Promise rejection made.

Uncaught (in promise) Error: Foo Rejected Promise.(…)

Firefox 看起来很像 Chrome,除了围绕“未捕获”错误的措辞。

但问题是我打算稍后处理这个“错误”,当时我正在使用承诺。仅仅有一个被拒绝的承诺不应该导致控制台错误......如果我消费这个承诺并且不处理错误就会发生这种情况。

要模拟“更晚”的错误处理,请考虑对代码进行以下更改:

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);

现在在这种情况下,我们通过在超时后在控制台上显示一些内容来“处理”错误。现在 IE 仍然按照我的预期处理这个问题:

About to make a promise rejection.

Promise rejection made.

Displaying Foo Rejected Promise. 10 seconds later.

在这种情况下,Firefox 确实像 IE,并且准确地显示了这些消息,并且没有给我一个错误的控制台错误。

Chrome,但是,还是报错:

About to make a promise rejection.

Promise rejection made.

Uncaught (in promise) Error: Foo Rejected Promise.(…)

Displaying Foo Rejected Promise. 10 seconds later.

所以Chrome既抱怨我的错误没有被处理,又显示处理了

看来我可以使用以下看起来像是 hack 的代码来解决所有这些问题。基本上我在创建承诺时对错误进行了“假”处理,然后按照我以后想要的方式真正处理它:

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);

但这是丑陋的代码。

我的问题是双重的 - 是否有某种方法可以查看 Chrome(以及某种程度上的 FireFox)正在做的事情并将其视为一种帮助?因为对我来说这看起来很糟糕。其次,有没有比假装处理错误更好的方法来解决这个问题?

提前致谢。

作为一个已经使用 promises 工作了几年的人,我理解为什么在 Chrome 中设置了这种行为。很容易忘记捕获被拒绝的 promise,如果 .then() 中有错误,将不会有任何通知,您的程序只会保持沉默。它可能很难调试。

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 

当忘记捕获错误时,这很容易造成非常模糊的行为。

在 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.

因此,这是 promise 中的一个已知问题。 Chrome 只是决定让 .done() 成为默认行为,要求您明确 .catch(_=>{}) 如果您想忽略失败的承诺。 Q 已决定将静默失败设置为默认设置,要求您明确使用 .done() 如果您希望在承诺失败时出现异常。如果您喜欢后一种方法,您总是可以包含一个 promise 库。恕我直言,这是一个品味问题。

任何有可能被拒绝的承诺都应该被处理,类似于异常。

不同步链接 promise 的可能性不大。但如果不会,则需要 'hack-ish' .catch(() => { }) 同步处理拒绝。

Chrome promise rejection behavior put this

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);

进入与

相同的错误处理船
try {
  throw new Error("Foo"));

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

异常不会等10秒被下一个try...catch捕捉到才被抛出吗?它甚至不会等待一个滴答声。它需要 'hack-ish' try { ... } catch (err) {} 块才能按预期执行。

这种行为早已为 Bluebird 库用户所熟知。 Bluebird 具有可调节的错误处理功能,即使在大规模情况下也能高效 debug promises

该行为在 Angular 2 开发中也是已知的,它被强制用于 Zone.js 库的承诺。

由于 promise 调试的价值有限且适用于开发环境,因此可以在生产构建中修改或禁用本机 promise 的此行为:

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

Here's more reading 关于这个问题。