我無法理解為什麼拒絕不透過承諾鏈傳遞,我希望有人能夠幫助我理解原因。對我來說,將功能附加到一系列承諾意味著我依賴要履行的原始承諾的意圖。這很難解釋,所以讓我先展示我的問題的程式碼範例。 (注意:本範例使用 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 的回复,我想我發現了我的邏輯錯誤。我想我可能忽略了另一個導致我遇到問題的問題。這可能導致承諾鏈的工作方式與我想像的不同。我仍在測試程式碼的不同元素,所以我甚至無法形成一個正確的問題來看看我做錯了什麼。不過,我確實想向大家通報最新情況,並感謝你們的幫忙。
@Jordan 首先,正如評論者指出的,當使用延遲函式庫時,您的第一個範例肯定會產生您期望的結果:
其次,即使它會產生您建議的輸出,它也不會影響第二個程式碼片段的執行流程,這有點不同,更像是:
並且,如果第一個承諾被拒絕,只會輸出:
然而(到達最有趣的部分)即使延遲庫肯定返回
3 x returned
,大多數其他承諾庫將返回1 x returned, 2 x 已解決
(這導致假設您透過使用其他一些Promise 程式庫獲得了這些結果)。另外令人困惑的是,其他函式庫的行為比較正確。讓我解釋一下。
在同步世界中,「承諾拒絕」的對應部分是
拋出
。因此從語義上講,同步中的非同步deferred.reject(new Error())
等於throw new Error()
。 在您的範例中,您不會在同步回調中拋出錯誤,您只是返回它們,因此您切換到成功流程,其中錯誤是成功值。為了確保拒絕進一步通過,您需要重新拋出錯誤:現在的問題是,為什麼延遲函式庫會將傳回的錯誤視為拒絕?
原因是延遲工作中的拒絕有點不同。在deferred lib 中,規則是:當出現錯誤實例時,promise 會被拒絕,因此即使你執行
deferred.resolve(new Error())
它也會起作用如deferred.reject(new Error()) ,如果你嘗試執行deferred.reject(notAnError) ,它會拋出一個異常,表示該Promise 只能被拒絕有錯誤的實例。這清楚地表明了為什麼從then
回呼返回的錯誤拒絕了承諾。延遲邏輯背後有一些有效的推理,但它仍然與 JavaScript 中
throw
的工作方式不符,因此,此行為計劃在延遲的 v0.7 版本中進行更改。簡短摘要:
為了避免混亂和意外結果,只需遵循良好實踐規則:
遵守上述規定,您將在延遲程式庫和其他流行的 Promise 庫中獲得一致且預期的結果。
沒有。您所描述的不是一個鏈,而只是將所有回調附加到
d1
。然而,如果您想使用then
連結某些內容,promise2
的結果取決於promise1
的分辨率以及promise1
的分辨率以及然後回調處理了它。文檔指出:
.then
方法通常根據 Promises 來看待/A 規範(或更嚴格的Promsises/A )。這意味著回調shell 返回的Promise 將被同化為Promise2 的解析,如果沒有成功/錯誤處理程序,相應的結果將直接傳遞給Promise2 code> - 因此您可以簡單地省略處理程序來傳播錯誤。但是,如果錯誤被處理,則產生的
promise2
被視為已修復,並將用該值來實現。如果您不希望發生這種情況,則必須重新拋出
錯誤,就像在 try-catch 子句中一樣。或者,您可以從處理程序傳回(待)拒絕的承諾。不確定 Dojo 的拒絕方式是什麼,但:他不應該能夠。如果沒有錯誤處理程序,他只會感知到沒有剩餘小部件的訊息(((來自約翰)來自Ginger)。然而,如果金格為這種情況設定了一個錯誤處理程序,如果約翰或他的經銷商沒有剩下藍色的小部件,她仍然可以履行她的承諾,從她自己的小屋裡給鮑勃一個小部件,給他一個綠色的小部件。
要將錯誤回調轉換為元,從處理程序返回錯誤就像說“如果沒有留下任何小部件,只需給他一個註釋,即沒有留下任何小部件- 這是與所需的小部件一樣好」。
...這表示錯誤已在那裡處理。如果您不這樣做,只需省略錯誤回調即可。順便說一句,您的成功回調不會
返回
它們正在創建的承諾,因此它們似乎毫無用處。正確的是:或者,由於您不需要閉包來存取先前回呼的結果值,甚至: