2012-11-21 8 views
10

Sto usando $.when per concatenare alcuni oggetti rinviati, e se uno di loro fallisce, il metodo always verrà chiamato direttamente dopo l'errore, anche se ho ancora qualche deferrer in uno stato "in sospeso".jquery posticipato - "sempre" chiamato al primo rigetto

var promises = [], defs = []; 

for(var i=0 ; i < 10 ; i++){ 
    defs.push($.Deferred()); 
    promises.push(defs[i].promise()); 
} 

var res = $.when.apply($, promises); 

res.fail(function(){console.log('failed')}); 
res.done(function(){console.log('done')}); 
res.always(function(){console.log('always')}); 
res.then(function(){console.log('then, done')},  
     function(){console.log('then, failed')});   

var j = 0;      
var t = setInterval(function(){ 
    if(j < 10){ 
     if(j < 5) { 
      console.log('resolve'); 
      defs[j++].resolve();  
     } 
     else { 
      console.log('reject'); 
      defs[j++].reject(); 
     } 
    } 
    else { 
     clearInterval(t);   
    } 
}, 200); 

Verificare this jsfiddle.

Forse è il comportamento normale. Ma, in questo caso, come posso prendere la fine della mia catena anche se alcuni di loro hanno fallito?

risposta

11

È di progettazione: il metodo risolverà il suo comando Rimandato non appena tutti i differiti risolvono, o rifiutano il master differito non appena uno uno dei differiti viene respinto. [...] Si noti che alcuni dei differiti potrebbero ancora essere irrisolti a quel punto.

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

È possibile salvare i riferimenti a tutte le deferreds e tenerne traccia separatamente.

Qualcosa di simile a questo:

var whenAll = function() { 
    var dfd = $.Deferred(), 
     len = arguments.length, 
     counter = 0, 
     state = "resolved", 
     resolveOrReject = function() { 
      if(this.state() === "rejected"){ 
       state = "rejected"; 
      } 
      counter++; 

      if(counter === len) { 
       dfd[state === "rejected"? "reject": "resolve"](); 
      } 

     }; 


    $.each(arguments, function(idx, item) { 
     item.always(resolveOrReject); 
    }); 

    return dfd.promise();  
}; 

http://jsfiddle.net/cSy2K/2/

+0

grazie @yury-tarabanko! Mi piace l'idea di estendere jQuery e aggiungere un metodo riutilizzabile per esaminare tutti i finali del deferrer, anche se falliscono. In base alla tua idea e alla sorgente "when" di jQuery, ho fatto di nuovo il "whenall", controlla la fonte se hai tempo http://jsfiddle.net/cSy2K/5/;) – zazabe

1

Sì, è un comportamento normale. Se uno fallisce, anche la cosa che fa affidamento su tutti fallisce. Vedi anche lo jQuery docs.

Quindi, hai per tenere traccia manualmente, o si alimentano promesse solo risolti in when:

promises.push(defs[i].promise().then(function(x) { 
     return {result:x,resolved:true}; 
    }, function(x) { 
     return (new $.Deferred).resolve({result:x,resolved:false}); 
    }) 
); 

Con questo, il vostro res chiamerà solo il done callback quando tutte le promesse sono trattati, e sarà ottenere una matrice di oggetti che indicano lo stato e il risultato di essi.

Problemi correlati