拒絕不會在鍊式承諾中傳播
P粉193307465
P粉193307465 2023-10-23 17:50:47
0
2
2860

我無法理解為什麼拒絕不透過承諾鏈傳遞,我希望有人能夠幫助我理解原因。對我來說,將功能附加到一系列承諾意味著我依賴要履行的原始承諾的意圖。這很難解釋,所以讓我先展示我的問題的程式碼範例。 (注意:本範例使用 Node 和延遲節點模組。我使用 Dojo 1.8.3 對此進行了測試,並得到了相同的結果)

var d = require("deferred");

var d1 = d();

var promise1 = d1.promise.then(
    function(wins) { console.log('promise1 resolved'); return wins;},
    function(err) { console.log('promise1 rejected'); return err;});
var promise2 = promise1.then(
    function(wins) { console.log('promise2 resolved'); return wins;},
    function(err) { console.log('promise2 rejected'); return err;});
var promise3 = promise2.then(
    function(wins) { console.log('promise3 resolved'); return wins;},
    function(err) { console.log('promise3 rejected'); return err;});
d1.reject(new Error());

執行此操作的結果是這樣的輸出:

promise1 rejected
promise2 resolved
promise3 resolved

好吧,對我來說,這個結果沒有意義。透過附加到這個 Promise 鏈,每個 then 都暗示著這樣的意圖:它將依賴 d1 的成功解析以及沿著鏈傳遞的結果。如果promise1中的promise沒有收到wins值,而是在其錯誤處理程序中收到一個err值,那麼鏈中的下一個promise怎麼可能呼叫它的success函數呢?它無法將有意義的值傳遞給下一個 Promise,因為它本身沒有獲得值。

我可以用另一種方​​式來描述我的想法:有三個人:John、Ginger 和 Bob。約翰擁有一家小部件商店。金傑走進他的店裡,要了一袋各種顏色的小部件。他沒有庫存,因此他向經銷商發送請求,要求將它們運送給他。同時,他給了金傑一張雨支票,說他欠她那袋小零件。鮑伯發現金傑正在獲取這些小部件,並要求他在她用完這些小部件後獲取藍色的小部件。她同意了,並給了他一張紙條,表示她會同意。現在,約翰的經銷商在他們的供應中找不到任何小部件,製造商不再生產這些小部件,因此他們通知約翰,約翰又通知金傑她無法獲得這些小部件。當 Bob 自己沒有得到任何東西時,他如何能夠從 Ginger 那裡得到藍色小部件?

我對這個問題的第三個更現實的觀點是這樣的。假設我有兩個值想要更新到資料庫。一個依賴另一個的 id,但在我將其插入資料庫並獲得結果之前,我無法取得 id。最重要的是,第一次插入取決於資料庫的查詢。資料庫呼叫返回的承諾是我用來將兩個呼叫連結成一個序列的。

var promise = db.query({parent_id: value});
promise.then(function(query_result) {
    var first_value = {
        parent_id: query_result[0].parent_id
    }
    var promise = db.put(first_value);
    promise.then(function(first_value_result) {
        var second_value = {
            reference_to_first_value_id: first_value_result.id
        }
        var promise = db.put(second_value);
        promise.then(function(second_value_result) {
            values_successfully_entered();
        }, function(err) { return err });
    }, function(err) { return err });
}, function(err) { return err });

現在,在這種情況下,如果 db.query 失敗,它將呼叫第一個 then 的 err 函數。但隨後它會呼叫下一個承諾的成功函數。雖然該 Promise 期望第一個值的結果,但它會從其錯誤處理函數中取得錯誤訊息。

所以,我的問題是,如果我必須測試成功函數中的錯誤,為什麼還要有錯誤處理函數?

抱歉,這篇文章太長了。我只是不知道如何用另一種方​​式解釋它。

更新與修正

#

(注意:我刪除了我曾經對某些評論做出的回應。因此,如果有人對我的回應發表評論,那麼既然我刪除了它,他們的評論可能會顯得斷章取義。對此表示抱歉,我試圖將其保留為盡可能短。)

謝謝大家的回覆。我首先想向大家道歉,因為我的問題寫得這麼差,尤其是我的偽代碼。我有點過於激進地試圖保持簡短。

感謝 Bergi 的回复,我想我發現了我的邏輯錯誤。我想我可能忽略了另一個導致我遇到問題的問題。這可能導致承諾鏈的工作方式與我想像的不同。我仍在測試程式碼的不同元素,所以我甚至無法形成一個正確的問題來看看我做錯了什麼。不過,我確實想向大家通報最新情況,並感謝你們的幫忙。

P粉193307465
P粉193307465

全部回覆(2)
P粉155710425

@Jordan 首先,正如評論者指出的,當使用延遲函式庫時,您的第一個範例肯定會產生您期望的結果:

promise1 rejected
promise2 rejected
promise3 rejected

其次,即使它會產生您建議的輸出,它也不會影響第二個程式碼片段的執行流程,這有點不同,更像是:

promise.then(function(first_value) {
    console.log('promise1 resolved');
    var promise = db.put(first_value);
    promise.then(function (second_value) {
         console.log('promise2 resolved');
         var promise = db.put(second_value);
         promise.then(
             function (wins) { console.log('promise3 resolved'); },
             function (err) { console.log('promise3 rejected'); return err; });
    }, function (err) { console.log('promise2 rejected'); return err;});
}, function (err) { console.log('promise1 rejected'); return err});

並且,如果第一個承諾被拒絕,只會輸出:

promise1 rejected

然而(到達最有趣的部分)即使延遲庫肯定返回3 x returned,大多數其他承諾庫將返回1 x returned, 2 x 已解決(這導致假設您透過使用其他一些Promise 程式庫獲得了這些結果)。

另外令人困惑的是,其他函式庫的行為比較正確。讓我解釋一下。

在同步世界中,「承諾拒絕」的對應部分是拋出。因此從語義上講,同步中的非同步 deferred.reject(new Error()) 等於 throw new Error()。 在您的範例中,您不會在同步回調中拋出錯誤,您只是返回它們,因此您切換到成功流程,其中錯誤是成功值。為了確保拒絕進一步通過,您需要重新拋出錯誤:

function (err) { console.log('promise1 rejected'); throw err; });

現在的問題是,為什麼延遲函式庫會將傳回的錯誤視為拒絕?

原因是延遲工作中的拒絕有點不同。在deferred lib 中,規則是:當出現錯誤實例時,promise 會被拒絕,因此即使你執行deferred.resolve(new Error()) 它也會起作用如deferred.reject(new Error()) ,如果你嘗試執行deferred.reject(notAnError) ,它會拋出一個異常,表示該Promise 只能被拒絕有錯誤的實例。這清楚地表明了為什麼從 then 回呼返回的錯誤拒絕了承諾。

延遲邏輯背後有一些有效的推理,但它仍然與 JavaScript 中 throw 的工作方式不符,因此,此行為計劃在延遲的 v0.7 版本中進行更改。

簡短摘要:

為了避免混亂和意外結果,只需遵循良好實踐規則:

  1. 總是拒絕帶有錯誤實例的承諾(遵循同步世界的規則,其中拋出非錯誤的值被視為不好的做法)。
  2. 透過拋出錯誤拒絕同步回呼(回傳錯誤並不能保證拒絕)。

遵守上述規定,您將在延遲程式庫和其他流行的 Promise 庫中獲得一致且預期的結果。

P粉376738875

沒有。您所描述的不是一個鏈,而只是將所有回調附加到d1。然而,如果您想使用then 連結某些內容,promise2 的結果取決於promise1 的分辨率以及promise1 的分辨率以及然後回調處理了它。

文檔指出:

.then 方法通常根據 Promises 來看待/A 規範(或更嚴格的Promsises/A )。這意味著回調shell 返回的Promise 將被同化為Promise2 的解析,如果沒有成功/錯誤處理程序,相應的結果將直接傳遞給Promise2 code> - 因此您可以簡單地省略處理程序來傳播錯誤。

但是,如果錯誤被處理,則產生的 promise2 被視為已修復,並將用該值來實現。如果您不希望發生這種情況,則必須重新拋出錯誤,就像在 try-catch 子句中一樣。或者,您可以從處理程序傳回(待)拒絕的承諾。不確定 Dojo 的拒絕方式是什麼,但:

var d1 = d();

var promise1 = d1.promise.then(
    function(wins) { console.log('promise1 resolved'); return wins;},
    function(err) { console.log('promise1 rejected'); throw err;});
var promise2 = promise1.then(
    function(wins) { console.log('promise2 resolved'); return wins;},
    function(err) { console.log('promise2 rejected'); throw err;});
var promise3 = promise2.then(
    function(wins) { console.log('promise3 resolved'); return wins;},
    function(err) { console.log('promise3 rejected'); throw err;});
d1.reject(new Error());

他不應該能夠。如果沒有錯誤處理程序,他只會感知到沒有剩餘小部件的訊息(((來自約翰)來自Ginger)。然而,如果金格為這種情況設定了一個錯誤處理程序,如果約翰或他的經銷商沒有剩下藍色的小部件,她仍然可以履行她的承諾,從她自己的小屋裡給鮑勃一個小部件,給他一個綠色的小部件。

要將錯誤回調轉換為元,從處理程序返回錯誤就像說“如果沒有留下任何小部件,只需給他一個註釋,即沒有留下任何小部件- 這是與所需的小部件一樣好」。

...這表示錯誤已在那裡處理。如果您不這樣做,只需省略錯誤回調即可。順便說一句,您的成功回調不會返回它們正在創建的承諾,因此它們似乎毫無用處。正確的是:

var promise = db.query({parent_id: value});
promise.then(function(query_result) {
    var first_value = {
        parent_id: query_result[0].parent_id
    }
    var promise = db.put(first_value);
    return promise.then(function(first_value_result) {
        var second_value = {
            reference_to_first_value_id: first_value_result.id
        }
        var promise = db.put(second_value);
        return promise.then(function(second_value_result) {
            return values_successfully_entered();
        });
    });
});

或者,由於您不需要閉包來存取先前回呼的結果值,甚至:

db.query({parent_id: value}).then(function(query_result) {
    return db.put({
        parent_id: query_result[0].parent_id
    });
}).then(function(first_value_result) {
    return db.put({
        reference_to_first_value_id: first_value_result.id
    });
}.then(values_successfully_entered);
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板