【问题标题】:Do native promises in JavaScript (ES6) have a default onFulfilled handler?JavaScript (ES6) 中的原生 Promise 是否有默认的 onFulfilled 处理程序?
【发布时间】:2015-07-27 23:06:28
【问题描述】:

我正在审查 ES6 中的 Promise 实现,并且想知道以下示例中的行为。在此示例中,我没有立即使用 then() 方法注册处理程序。这样做(在 Chrome 43 中)会在 promise 被拒绝时在控制台中记录一个错误,但处理程序仍在执行。

我的假设是我收到错误是因为在我附加处理程序之前承诺被拒绝(由于 setTimeout)。但是按照这种逻辑,当确定要解决承诺时,Chrome 不应该也记录一个错误吗?这是怎么回事?

var par = document.querySelector('p');

var P = new Promise(function(resolve, reject){
    var v = Math.random();
    if(v < 0.5){resolve(v)}
    else{reject(v)}
});


//wait to attach handlers
setTimeout(function(){
    var n = P.then(
        function(v){
            par.innerHTML = "Good, "+v+" is less than 0.5.";
        },
        function(v){
            par.innerHTML = "Uh oh, "+v+" is greater than or equal to 0.5.";
        }
    )
},1000);
&lt;p&gt;&lt;/p&gt;

【问题讨论】:

  • Chrome 的“本机”承诺行为不正确
  • “当确定要解决承诺时,Chrome不应该也抛出错误吗?”是什么意思?
  • @JaromandaX 关心扩展吗?虽然这种行为不在规范中,但它很常见,我不确定我是否会将其归类为“不正确”。
  • @loganfsmyth - 我曾经使用过(至少声称)符合 Promise/A+ 规范的每个 Promise 库都不需要立即使用 .catch() 来避免未捕获的异常,如果承诺在调用任何 then 方法之前被拒绝。事实上,在那之前的解决办法很好,在这方面拒绝应该没有区别
  • 没有什么不正常的。这是预期行为,目前正在标准化。

标签: javascript promise ecmascript-6


【解决方案1】:

您遇到了未处理的拒绝检测

这基本上是 Chrome 的支持。在实际的 Promise 用法中,您几乎从不异步附加拒绝处理程序。拒绝很少见(如异常),如果它可能拒绝,你永远不想让承诺“挂起”而没有错误处理程序。

Chrome 让您知道您有一个拒绝,因为您没有同步附加处理程序 *.这是为了让拒绝在以下情况下被吞下:

Promise.resolve().then(function(){
    var obj = JSON.prase("{}"); // note the typo
    console.log(obj);
});

如果 Chrome 不这样做并且这是一个嵌套链 - 您会遇到一个非常难以调试的错误。 Chrome 这样做是一件非常好的事情,而且它目前正在标准化,所以所有浏览器都这样做。

故事的寓意是:

始终同步附加拒绝处理程序。

很少有理由不这样做。

(*) 实际算法在微任务中,但我们忽略它。

这里是the specification,如果你好奇的话:https://github.com/domenic/unhandled-rejections-browser-spec

这里是 Node/io.js 的规范:https://gist.github.com/benjamingr/0237932cee84712951a2

【讨论】:

    【解决方案2】:

    这只是为了方便开发人员而可能无法处理错误的代码的故障保护。它不应该影响其他代码,并且只在控制台和调试器中可见。如果在同步代码中抛出错误但未被捕获,则默认行为是将该错误记录到控制台。使用 Promise,通过拒绝 Promise 来捕获和处理抛出的错误。由于 JS 引擎无法轻易知道将来是否会处理被拒绝的 Promise,如果一个 Promise 被拒绝,并且在被拒绝时没有处理程序,大多数浏览器都会向控制台记录一条消息。

    如果您想防止这种情况发生,您可以执行与同步代码类似的代码。在同步代码中有 try/catch 的地方,您可以使用无操作的 .catch() 处理程序来处理 Promise。

    var par = document.querySelector('p');
    
    var P = new Promise(function(resolve, reject){
        var v = Math.random();
        if(v < 0.5){resolve(v)}
        else{reject(v)}
    });
    P.catch(function(reason){
        // noop
    });
    
    //wait to attach handlers
    setTimeout(function(){
        var n = P.then(
            function(v){
                par.innerHTML = "Good, "+v+" is less than 0.5.";
            },
            function(v){
                par.innerHTML = "Uh oh, "+v+" is greater than or equal to 0.5.";
            }
        )
    },1000);
    &lt;p&gt;&lt;/p&gt;

    【讨论】:

      【解决方案3】:

      我建议您重新编写代码以始终如一地使用 Promise。这是承诺和回调之间的互操作可能会导致麻烦的情况之一。 Promise 是要被链接的,如果你发现你有充分的理由在很长一段时间后链接它们,你应该考虑是否可以重新编写代码以同步设置链。

      例如,在这种情况下,您需要翻转 setTimeout

      function delay(ms){
          return new Promise(function(resolve, reject){
              setTimeout(resolve, ms);
          });
      }
      
      var par = document.querySelector('p');
      
      var P = new Promise(function(resolve, reject){
          var v = Math.random();
          if(v < 0.5){resolve(v)}
          else{reject(v)}
      });
      
      P.then(
          function(result){
              return delay(1000).then(function(){ return result; });
          },
          function(err){
              return delay(1000).then(function(){ throw err; });
          }
      ).then(
          function(v){
              par.innerHTML = "Good, "+v+" is less than 0.5.";
          },
          function(v){
              par.innerHTML = "Uh oh, "+v+" is greater than or equal to 0.5.";
          }
      )
      

      【讨论】:

        猜你喜欢
        • 2016-06-02
        • 2021-01-13
        • 2010-11-03
        • 1970-01-01
        • 2019-01-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多