簡體   English   中英

JavaScript(ES6)中的本機承諾是否具有默認的onFulfilled處理程序?

[英]Do native promises in JavaScript (ES6) have a default onFulfilled handler?

我正在審查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); 
 <p></p> 

您遇到了未處理的拒絕檢測

基本上這是Chrome的后盾。 在實際的Promise用法中,您幾乎永遠不會異步附加拒絕處理程序。 拒絕是很少見的(像異常一樣),並且如果它可能會拒絕,則您永遠都不想在沒有錯誤處理程序的情況下留下承諾“掛起”。

Chrome讓您知道您拒絕了未附加用於同步*的處理程序。 這是為了在以下情況下吞噬拒收物:

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

如果Chrome瀏覽器沒有執行此操作,而這是一個嵌套的鏈-您將很難調試錯誤。 Chrome確實可以做到這一點,而且目前已被標准化,因此所有瀏覽器都可以做到這一點。

這個故事的寓意是:

始終同步附加拒絕處理程序。

很少有理由不這樣做。

(*)實際的算法在微任務中,但是我們忽略它。

如果您好奇的話,這里是規格https : //github.com/domenic/unhandled-rejections-browser-spec

這是Node / io.js的規范: https ://gist.github.com/benjamingr/0237932cee84712951a2

對於可能無法為開發人員提供便利的錯誤而處理的代碼,這只是故障保護。 它不應影響其他代碼,並且僅在控制台和調試器中可見。 如果在同步代碼中引發了錯誤但未捕獲到錯誤,則默認行為是將該錯誤記錄到控制台。 有了諾言,就可以通過拒絕諾言來捕獲和處理拋出的錯誤。 由於JS引擎無法輕易知道將來是否會處理被拒絕的承諾,因此,如果一個承諾被拒絕,並且被拒絕時沒有處理程序,那么大多數瀏覽器會將消息記錄到控制台。

如果要防止這種情況,可以執行與同步代碼類似的代碼。 在同步代碼中有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> 

我建議您對您的代碼進行重做以一致地使用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.";
    }
)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM