2011-04-01 73 views
22

當一個延遲操作失敗時使用$.when()時,我收到了意外的結果。

拿這個JavaScript,它創建了2個延期。第一個成功,第二個失敗。

var f1 = function() { 
    return $.Deferred(function(dfd) { 
     dfd.resolve('123 from f1'); 
    }).promise(); 
}; 

var f2 = function() { 
    return $.Deferred(function(dfd) { 
     dfd.reject('456 from f2'); 
    }).promise(); 
}; 

$.when(f1(), f2()) 
    .then(function(f1Val, f2Val) { 
     alert('success! f1, f2: ' + JSON.stringify([f1Val, f2Val])); 
    }) 
    .fail(function(f1Val, f2Val) { 
     alert('fail! f1, f2: ' + JSON.stringify([f1Val, f2Val])); 
    }); 

運行它自己:http://jsfiddle.net/r2d3j/2/

我得到fail! f1, f2: ["456 from f2", null]

的問題是,在.fail()回調與f2()拒絕傳遞的值,被路由到第一個參數,在這裏我希望f1Value。這意味着我真的沒有辦法知道哪個延期對象實際上發佈了reject(),我也不知道哪個失敗數據實際屬於哪個操作。

我本來預計.fail()會得到參數null, '456 from f2',因爲第一次延期沒有失敗。或者我只是在這裏沒有以正確的方式做延期?

如何知道哪些延遲失敗,哪些拒絕參數屬於哪個延遲失敗,如果回調中的參數順序不受尊重?

回答

5

在內部,「拒絕」和「失敗」路徑由兩個完全分開的隊列處理,所以它不會像您期望的那樣工作。

爲了從「when()」組中知道哪個Deferred原始失敗,您可以讓它們自己與「.reject()」調用一起作爲對象字面量的一部分。

+0

嗯,不完全如何我會設計它,但我想這使得我的結果是有道理的。 – 2011-04-01 21:27:11

21

$.when()將立即執行失敗的回調(第二個參數傳遞給then()),如果任何一個參數失敗。這是設計。引述文件:

http://api.jquery.com/jQuery.when/

在該Deferreds的一個被拒絕的多Deferreds情況下,jQuery.when立即觸發了它的主人已延期failCallbacks。請注意,在這一點上,某些延期付款可能仍未解決。如果您需要爲這種情況執行額外的處理,例如取消任何未完成的ajax請求,則可以將引用保留在關閉中的基礎jqXHR對象中,並在failCallback中檢查/取消它們。

實際上沒有獲取回調的內置方式,等待所有這些回調完成,無論其成功/失敗狀態如何。

所以,我建立了一個$.whenAll()你:)
它總是等待,直到所有的人解決,一個或其他方式:

http://jsfiddle.net/InfinitiesLoop/yQsYK/51/

$.whenAll(a, b, c) 
    .then(callbackUponAllResolvedOrRejected); 
+1

你說「將立即執行then()回調」,但是你所包含的引用說「立即激發failCallbacks」 - 那它是什麼? '\ - :' – hippietrail 2012-08-08 10:12:23

+2

@hippietrail它是兩個。那麼()在延期被解決或被拒絕時觸發。這被歸類爲拒絕,因此它被激發。 – InfinitiesLoop 2012-08-08 16:16:46

+2

不,它不會觸發當時的回調。它觸發失敗回調。 (技術上可以是傳遞給'then'的'second'回調函數,但無論如何,它與'then'回調函數不同,大多數人閱讀你的答案時都會假設'成功'回調函數)。 – hasen 2014-01-02 21:31:29

0

我面臨同樣的問題,我通過使用.always回調和檢查我的延期對象數組來處理它。我有數目不詳的ajax調用的,所以我不得不做以下幾點:

// array of ajax deletes 
var deletes = []; 
$checkboxes.each(function() { 
    deletes.push(deleteFile(this)); 
}); 

$.when.apply($, deletes) 
    .always(function() { 
     // unfortunately .fail shortcircuits and returns the first fail, 
     // so we have to loop the deferred objects and see what happened. 

     $.each(deletes, function() { 
      this.done(function() { 
       console.log("done"); 
      }).fail(function() { 
       console.log("fail"); 
      }); 
     }); 
    }); 

的DELETEFILE方法返回一個承諾,其.done或.fail回調。

這使您可以在所有延期完成後採取行動。在我的情況下,我將顯示刪除文件錯誤摘要。

我剛剛嘗試過這一點,不幸的是我不得不放置一個間隔計時器來檢查延遲對象上的$ .each後它們是否全部真正完成。這看起來很奇怪並且違反直覺。

仍試圖瞭解這些延期!

+0

總是會「短路」。 – Chloraphil 2013-08-06 18:07:10

0

http://jsfiddle.net/InfinitiesLoop/yQsYK/

這將始終拒絕如果有多個輸入。 rejected = true;應該是rejected |= reject;

+0

謝謝,我只是現在注意到這個答覆,因爲它沒有在我的答案。我修好了......再晚一點,永遠不會? – InfinitiesLoop 2014-01-03 03:40:07