2015-01-25 5 views
6

Ich versuche, eine asynchrone Schleife mit nativen ES6 promises Es Art von funktioniert, aber falsch. Ich glaube, ich machte einen großen Fehler irgendwo und ich brauche jemanden, der mir zu sagen, wo es ist und wie es richtigSchleife mit nativen Versprechungen;

var i = 0; 

//creates sample resolver 
function payloadGenerator(){ 
    return function(resolve) { 
     setTimeout(function(){ 
      i++; 
      resolve(); 
     }, 300) 
    } 
} 

// creates resolver that fulfills the promise if condition is false, otherwise rejects the promise. 
// Used only for routing purpose 
function controller(condition){ 
    return function(resolve, reject) { 
     console.log('i =', i); 
     condition ? reject('fin') : resolve(); 
    } 
} 

// creates resolver that ties payload and controller together 
// When controller rejects its promise, main fulfills its thus exiting the loop 
function main(){ 
    return function(resolve, reject) { 
     return new Promise(payloadGenerator()) 
      .then(function(){ 
       return new Promise(controller(i>6)) 
      }) 
      .then(main(),function (err) { 
       console.log(err); 
       resolve(err) 
      }) 
      .catch(function (err) { 
       console.log(err , 'caught'); 
       resolve(err) 
      }) 
    } 
} 


new Promise(main()) 
    .catch(function(err){ 
     console.log('caught', err); 
    }) 
    .then(function(){ 
     console.log('exit'); 
     process.exit() 
    }); 

nun den Ausgang getan:

/usr/local/bin/iojs test.js 
i = 1 
i = 2 
i = 3 
i = 4 
i = 5 
i = 6 
i = 7 
fin 
error: [TypeError: undefined is not a function] 
error: [TypeError: undefined is not a function] 
error: [TypeError: undefined is not a function] 
error: [TypeError: undefined is not a function] 
error: [TypeError: undefined is not a function] 
error: [TypeError: undefined is not a function] 
error: [TypeError: undefined is not a function] 
caught [TypeError: undefined is not a function] 
exit 

Process finished with exit code 0 

Das gute Teil: es das Ende erreicht.

Der schlechte Teil: Es fängt einige Fehler und ich weiß nicht warum.

+1

Unabhängig davon, welche Bibliotheken verwendet werden, ist die Art, wie Sie Versprechen verwenden, _really_ seltsam. Was ist dein Endziel hier? Sie möchten eine "Weile" mit Versprechen umsetzen? –

+1

'.then (main(), function (err) {'. Wann ist das 'main()' da? –

+0

Du hast vergessen zu sagen, was dieser Code machen soll. – JLRishe

Antwort

7

Jede Hilfsfunktion mit Versprechen Schleife habe ich gesehen habe es tatsächlich viel schlimmer gemacht als das, was Sie aus der Box mit Rekursion tun können.

Es ist ein wenig schöner mit .thenReturn aber ja:

function readFile(index) { 
    return new Promise(function(resolve) { 
     setTimeout(function() { 
      console.log("Read file number " + (index +1)); 
      resolve(); 
     }, 500); 
    }); 
} 

// The loop initialization 
Promise.resolve(0).then(function loop(i) { 
    // The loop check 
    if (i < len) {    // The post iteration increment 
     return readFile(i).thenReturn(i + 1).then(loop); 
    } 
}).then(function() { 
    console.log("done"); 
}).catch(function(e) { 
    console.log("error", e); 
}); 

es Siehe in jsfiddle http://jsfiddle.net/fd1wc1ra/

Das ist so ziemlich genau entspricht:

try { 
    for (var i = 0; i < len; ++i) { 
     readFile(i); 
    } 
    console.log("done"); 
} catch (e) { 
    console.log("error", e); 
} 

Wenn Sie wollte verschachtelte Schleifen tun, es ist genau das gleiche:

http://jsfiddle.net/fd1wc1ra/1/

function printItem(item) { 
    return new Promise(function(resolve) { 
     setTimeout(function() { 
      console.log("Item " + item); 
      resolve(); 
     }, 500); 
    }); 
} 

var mdArray = [[1,2], [3,4], [5,6]]; 
Promise.resolve(0).then(function loop(i) { 
    if (i < mdArray.length) { 
     var array = mdArray[i]; 
     return Promise.resolve(0).then(function innerLoop(j) { 
      if (j < array.length) { 
       var item = array[j]; 
       return printItem(item).thenReturn(j + 1).then(innerLoop); 
      } 
     }).thenReturn(i + 1).then(loop); 
    } 
}).then(function() { 
    console.log("done"); 
}).catch(function(e) { 
    console.log("error", e); 
}); 
+0

Großartig, danke! Schöner Trick mit 'thenReturn'. Eine Bemerkung: Ihr Code hier unterscheidet sich von Code auf jsfiddle, ich war zuerst verwirrt. jsfiddle Version funktionierte für mich, da iojs 'Versprechen Implementierung nicht 'thenReturn' hat. – amdc

+0

@amdc' thenReturn' stammt aus Bluebird, kann aber auf native Versprechungen implementiert werden wie in der jsifddle gezeigt – Esailija

+0

Funktioniert sehr gut! Ich möchte in der Lage sein, 5 Schleifen gleichzeitig zu machen, und wenn einer von denen fertig ist, gehen Sie mit dem 6. usw. weiter, bis das Array fertig ist. Irgendwelche Vorschläge? Kasse http: //stackoverflow.com/q/36664272/1760313 – Tom

0

Versuchen Sie Logging err.stack anstelle von nur err beim Abfangen Versprechen Fehler.

In diesem Fall sieht es so aus, als ob resolve und reject nicht in der anonymen Funktion definiert sind, die nach Abschluss der ersten Iteration eine Rückgabe von main erhält. Ich kann Ihrem Kontrollfluss nicht ganz folgen, aber das scheint Sinn zu ergeben - nach den 7 Iterationen sollte es keine neuen Versprechen mehr geben. Es sieht jedoch so aus, als würde der Code immer noch versuchen zu laufen, als ob es mehr Versprechungen zu lösen gäbe.

Bearbeiten: Dies ist das Problem .then(main(),function (err) {. Der Aufruf von main allein führt dazu, dass resolve und reject in der anonymen Funktion nicht definiert sind. Von der Art, wie ich es gelesen habe, main kann nur als ein Argument für den Promise Konstruktor aufgerufen werden.

2

Wenn alles, was Sie versuchen, auf 7 mit dem Versprechen zählen zu tun, dann wird dies es tun:

function f(p, i) { 
    return p.then(function() { 
    return new Promise(function(r) { return setTimeout(r, 300); }); 
    }) 
    .then(function() { console.log(i); }); 
} 

var p = Promise.resolve(); 
for (var i = 0; i < 8; i++) { 
    p = f(p, i); 
} 
p.then(function() { console.log('fin'); }) 
.catch(function(e) { console.log(e.message); }); 

mit dem Versprechen Looping hart ist, weil es fast unmöglich ist, nicht in JavaScript closures in a loop trap fallen, aber es ist machbar. Das obige funktioniert, weil es die gesamte Verwendung von .then() in eine Unterfunktion f der Schleife (d. H. Weg von der Schleife) drückt.

Eine sicherere Lösung, die ich benutze, ist Schleifen ganz zu verzichten und wie forEach Muster suchen und reduce wann immer ich kann, weil sie die Unterfunktion auf Sie effektiv erzwingen:

[0,1,2,3,4,5,6,7].reduce(f, Promise.resolve()) 
.then(function() { console.log('fin'); }) 
.catch(function(e) { console.log(e.message); }); 

hier f ist die gleiche Funktion wie oben. Try it.

Update:In ES6 können Sie auch for (let i = 0; i < 8; i++) verwenden, um die „Verschlüsse in einer Schleife“ trap ohne Code f in eine Unterfunktion schieben zu vermeiden.

PS: Der Fehler in Ihrem Beispiel ist .then(main(), - es braucht .then(function() { return new Promise(main()); }, aber wirklich zu sein, ich glaube, Sie verwenden das Muster falsch. main() sollte zurückgeben ein Versprechen, nicht von einem eingewickelt werden.

+0

Dies ist eine for-Schleife - ich denke, OP ging für eine 'while', die nicht mit später Chaining arbeiten würde, da Sie nicht wissen würden, wie viel Verkettung Sie im Voraus benötigen. –

+0

Vielleicht bedeutete das OP, dass die Bedingung davon abhing, dass die vorherigen Schritte ausgeführt wurden? Wenn ja, dann wäre eine Rekursion erforderlich. In jedem Fall löst das Verwenden einer while-Schleife anstelle von for-loop das nicht oder bedeutet das.Ich habe die for-Schleife in eine while-Schleife geändert, um das zu zeigen. Der OP sagte in einem Kommentar, er versuche, mit Versprechungen bis 7 zu zählen, und bat jemanden, auf seinen Fehler hinzuweisen, was ich hoffentlich tat. – jib

+0

Natürlich würde die Änderung der for-Schleife zu einem anderen synchronen Schleifenkonstrukt nicht helfen - ich meinte, dass konzeptionell die Anzahl der Iterationen vom vorherigen Zustand abhängt. –

0

Ich habe mich auch nach verschiedenen Lösungen umgesehen und konnte keine finden, die mich zufrieden stellten, also habe ich meine eigene erstellt. Hier ist es für den Fall, dass es für jemand anderen nützlich ist:

Die Idee ist, ein Array von Versprechen Generatoren zu erstellen und dieses Array zu einer Hilfsfunktion geben, die die Versprechen nacheinander ausführen wird.

In meinem Fall die Hilfsfunktion einfach ist das:

function promiseChain(chain) { 
    let output = new Promise((resolve, reject) => { resolve(); }); 
    for (let i = 0; i < chain.length; i++) { 
     let f = chain[i]; 
     output = output.then(f); 
    } 
    return output; 
} 

Dann zum Beispiel mehrere URLs nacheinander zu laden, würde der Code wie folgt sein:

// First build the array of promise generators: 

let urls = [......]; 
let chain = []; 
for (let i = 0; i < urls.length; i++) { 
    chain.push(() => { 
     return fetch(urls[i]); 
    }); 
} 

// Then execute the promises one after another: 

promiseChain(chain).then(() => { 
    console.info('All done'); 
}); 

Der Vorteil Bei diesem Ansatz wird Code erstellt, der relativ nahe an einer regulären for-Schleife und mit minimaler Einrückung liegt.

0

Ich hatte eine ähnliche Notwendigkeit und versuchte die angenommene Antwort, aber ich hatte ein Problem mit der Reihenfolge der Operationen. Promise.all ist die Lösung.

function work(context) { 
    return new Promise((resolve, reject) => { 
    operation(context) 
     .then(result => resolve(result) 
     .catch(err => reject(err)); 
    }); 
} 

Promise 
    .all(arrayOfContext.map(context => work(context))) 
    .then(results => console.log(results)) 
    .catch(err => console.error(err));