2017-02-06 1 views
1

Gibt es einen Sinn, dies mit anderen Lösungen eleganter zu machen (jquery ist in Ordnung)?Erzeugt aus sequentiellen Array-Ausführungen mit Deferred (jquery)

Wichtig in meinem Fall ist, dass entsprechende Ausführungen nur ausgelöst werden, wenn die vorherigen beendet wurden.

seqExe(["item one", "item two", "item three", "item four", "item five"]) 
 

 
function seqExe(corpus) { 
 
    var i = -1, 
 
     len = corpus.length, 
 
     defer = jQuery.Deferred(), 
 
     promise = defer.promise(); 
 

 
    while(++i < len) { 
 
     promise = promise.then((function(item) { 
 
      return function() { 
 
       console.log(item); 
 
       return foo(item); 
 
      } 
 
     }).call(this, corpus[i])); 
 
    } 
 

 
    promise.then(function() { 
 
     console.log("Done"); 
 
    }, function() { 
 
     console.error("Failed"); 
 
    }); 
 

 
    return defer.resolve(); 
 
} 
 

 
function foo(item) { 
 
    var defer = jQuery.Deferred(); 
 
    window.setTimeout(
 
     function() { 
 
      defer.resolve() 
 
      console.log(item); 
 
     }, Math.random() * 2000 + 1000); 
 

 
    return defer.promise(); 
 
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>

sollte Vermeintliche ausgegeben werden:

item one 
item one 
item two 
item two 
item three 
item three 
item four 
item four 
item five 
item five 
Done 

Antwort

2

Dies ist ein idealer Kandidat reduce für die Verwendung. Weiterhin Ihre erste Versprechen - die Sie sofort lösen wollen - können nur $.when() sein und wird der Startwert des reduce Aufruf werden:

seqExe(["item one", "item two", "item three", "item four", "item five"]) 
 

 
function seqExe(corpus) { 
 
    return corpus.reduce(function (p, item) { 
 
     return p.then(function() { 
 
      console.log(item + ' pending'); 
 
      return foo(item); 
 
     }); 
 
    }, $.when()) 
 
    .then(function(){ 
 
     console.log("Done"); 
 
    }, function(){ 
 
     console.log("Failed"); 
 
    }); 
 
} 
 

 
function foo(item) { 
 
    var defer = new $.Deferred(); 
 
    window.setTimeout(
 
     function() { 
 
      defer.resolve() 
 
      console.log(item + ' resolved'); 
 
     }, Math.random() * 2000 + 1000); 
 
    return defer.promise(); 
 
}
<script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>

Beachten Sie, dass in einigen jQuery Versionen (Ich sehe das in 2.1.1) die Callbacks then werden synchron ausgeführt, die die falsche Reihenfolge der Ausgabe geben würden. Dies ist in jQuery 3 behoben (Ich habe 3.1.1 versucht, mit dem ich auch im obigen Snippet verlinkt habe).

Hier ist, wie Sie es mit nativem ES6 Versprechen tun würde.

seqExe(["item one", "item two", "item three", "item four", "item five"]) 
 

 
function seqExe(corpus) { 
 
    return corpus.reduce(function (p, item) { 
 
     return p.then(function() { 
 
      console.log(item + ' pending'); 
 
      return foo(item); 
 
     }); 
 
    }, Promise.resolve()) 
 
    .then(function(){ 
 
     console.log("Done"); 
 
    }, function(){ 
 
     console.log("Failed"); 
 
    }); 
 
} 
 

 
function foo(item) { 
 
    return new Promise(function (resolve) { 
 
     window.setTimeout(function() { 
 
      resolve(); 
 
      console.debug(item + ' resolved'); 
 
     }, Math.random() * 2000 + 1000); 
 
    }); 
 
}

+0

Was meinen Sie mit „* dann werden Rückrufe synchron ausgeführt, die die falsche Ausgabereihenfolge geben würde * "? Das kann nur bei bereits erfüllten Versprechen passieren, d. H. '$ .when()', aber alles nach dem ersten 'foo()' Aufruf ist verkettet. – Bergi

+0

@Bergi, ich meinte, wenn ich in den Snippet, den ich gepostet habe, habe ich mit jQuery Version 2.1.1 statt 3.1.1 verbunden, änderte sich die Reihenfolge der Ausgabe. NB: Ich habe jetzt eine Korrektur vorgenommen, weil ich zuerst das zweite console.log verschoben und vergessen habe, es wieder einzusetzen (nach der Auflösung). – trincot

+0

Ah, das. Richtig, du müsstest den 'console.debug' vor dem' deferred.resolve() 'Aufruf dafür platzieren. – Bergi

Verwandte Themen