JavaScript(ES6)中的本机承诺是否具有默认的onFulfilled处理程序?

时间:2015-07-27 23:06:29

标签: javascript promise ecmascript-6

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

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

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

3 个答案:

答案 0 :(得分:4)

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

这基本上就是Chrome。在实际的承诺使用中,您几乎从不异步附加拒绝处理程序。拒绝是罕见的(例外情况),你永远不想留下承诺&#34;挂&#34;没有错误处理程序,如果它可能拒绝。

Chrome会告诉您,如果您未同步附加处理程序,则表示您拒绝*。这是为了在以下情况下拒绝网络被拒绝

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

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

故事的寓意是:

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

很少有理由不这样做。

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

如果您有点好奇,请the specificationhttps://github.com/domenic/unhandled-rejections-browser-spec

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

答案 1 :(得分:1)

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

如果要阻止这种情况,可以使用与同步代码相同的代码。如果您在同步代码中拥有try / catch,则可以使用no-op .catch()处理程序进行承诺。

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

答案 2 :(得分:1)

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

例如,在这种情况下,您需要翻转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.";
    }
)