2016-10-25 5 views
4

Versuchen, einige moderne JS und insbesondere die ECMAScript 6 Promises zu lernen. Ich spiele mit diesem einfachen Test:Wie garantiere ich die Reihenfolge der Auflösung mehrerer Versprechen?

let slow = new Promise((resolve) => { 
 
    setTimeout(function() 
 
    { 
 
\t console.log('slow'); 
 
\t resolve(); \t 
 
    }, 2000, 'slow'); 
 
}); 
 

 
let instant = new Promise((resolve) => { 
 
\t console.log('instant'); 
 
\t resolve(); \t 
 
}); 
 

 
let quick = new Promise((resolve) => { 
 
    setTimeout(function() 
 
    { 
 
\t console.log('quick'); 
 
\t resolve(); \t 
 
    }, 1000, 'quick'); 
 
}); 
 

 
Promise.all([slow, instant, quick]).then(function(results) { 
 
    console.log('finished'); 
 
}, function(error) { 
 
    console.log(error); \t 
 
});

Was ich hier wollen alle Versprechen zu starten ist async zugleich. Und log, wenn sie alle fertig sind. In der Konsole wird dies wie erwartet angezeigt: "instant", "quick", "slow" und "finished".

Was aber, wenn ich sicher gehen wollte, dass "instant" nicht echo/log, bevor "slow" abgeschlossen wurde? Das heißt, ich möchte, dass die Konsole "schnell", "langsam", "sofort" und "fertig" protokolliert, aber gleichzeitig müssen sie alle gleichzeitig mit async starten.

Wie kann ich das erreichen? mit Ihrem aktuellen Code

+2

Versprechen Verkettungs, promise.all nicht –

+1

Kette die Versprechungen mit .then() –

+1

ich meine Frage aktualisiert haben, da würde das nicht bedeuten, dass die Versprechungen synchron laufen ? Ich brauche sie gleichzeitig, asynchron, was Promise.all mir gibt, glaube ich. Stellen Sie sich zum Beispiel vor, dass die 3 Versprechen einige Daten abrufen, aber ich muss sicher sein, dass die Daten in einer bestimmten Reihenfolge angezeigt werden. – Werner

Antwort

2

Also, um klar zu sein, was Sie hier tun wollen, ist es, alle Versprechen auf einmal zu starten und die Ergebnisse jedes Versprechens in einer bestimmten Reihenfolge anzuzeigen, wenn sie reinkommen, richtig?

In diesem Fall würde ich es wahrscheinlich wie folgt aus:

let slow = new Promise((resolve) => { 
 
    setTimeout(function() 
 
    { 
 
    // Rather than log here, we resolve to the value we want to log 
 
    resolve('slow'); 
 
    }, 2000, 'slow'); 
 
}); 
 

 
let instant = new Promise((resolve) => { 
 
    resolve('instant'); 
 
}); 
 

 
let quick = new Promise((resolve) => { 
 
    setTimeout(function() 
 
    { 
 
    resolve('quick'); 
 
    }, 1000, 'quick'); 
 
}); 
 

 
// All Promises are now running. Let's print the results... 
 

 
// First wait for the result of `slow`... 
 
slow.then((result) => { 
 
    // Result received... 
 
    console.log(result); 
 
    
 
    // Now wait for the result of instant... 
 
    instant.then((result) => { 
 
    
 
    // Result received... 
 
    console.log(result); 
 
    
 
    // Now wait for the result of quick... 
 
    quick.then((result) => { 
 
     
 
     // Result received... 
 
     console.log(result); 
 
     
 
    }).then((result) => { 
 
     // Done 
 
     console.log('finished'); 
 
    }); 
 
    }); 
 
});

Beachten Sie, dass im Gegensatz zu cchamberlain ‚s answer, diese Methode funktioniert nicht warten, bis alle Versprechen zu lösen bevor es beginnt, Ergebnisse zu liefern. Sie gibt die Ergebnisse so zurück, wie sie eingehen, ohne jedoch Ihre Anforderung zu verletzen, die Ergebnisse in der richtigen Reihenfolge zu halten. (Um dies zu überprüfen, versuchen Sie, die Wartezeit von quick auf 2500 ms zu ändern, und beachten Sie, dass das Ergebnis 500ms nach instant gedruckt wird.) Dies kann je nach Anwendung wünschenswert sein.

Der obige Code ist ein bisschen chaotisch, aber zum Glück mit der neuen async/await Syntax in ES2017 kann es viel sauberer gemacht werden:

let slow = new Promise((resolve) => { 
 
    setTimeout(function() 
 
    { 
 
    // Rather than log here, we resolve to the value we want to log 
 
    resolve('slow'); 
 
    }, 2000, 'slow'); 
 
}); 
 

 
let instant = new Promise((resolve) => { 
 
    resolve('instant'); 
 
}); 
 

 
let quick = new Promise((resolve) => { 
 
    setTimeout(function() 
 
    { 
 
    resolve('quick'); 
 
    }, 1000, 'quick'); 
 
}); 
 

 
// All Promises are now running. Let's print the results... 
 

 
async function logResults(...promises) { 
 
    for (let promise of promises) { 
 
    console.log(await promise); 
 
    } 
 
} 
 

 
logResults(slow, instant, quick).then(() => console.log('finished'));

Try in Babel. Hinweis: Der obige Code funktioniert derzeit nicht in modernen Browsern ohne Babel (Stand: Oktober 2016). In zukünftigen Browsern wird es.

+0

Wie unterscheidet sich das erste Beispiel von [dies] (http://stackoverflow.com/a/40248279/2801559) Antwort? 'new Promise()' wird aufgerufen, wenn 'instant' dem Ergebnis des 'new Promise()' -Konstruktors zugewiesen wird. – guest271314

+0

@ guest271314 Mit dieser Antwort werden alle asynchronen Vorgänge gleichzeitig gestartet, anstatt darauf zu warten, dass "langsam" abgeschlossen ist, bevor "schnell" ausgeführt wird, wie es bei dieser Antwort der Fall ist. – Ajedi32

+0

@ Ajedi32 das ist genau das, was ich gesucht habe. Sowohl das erste Beispiel als auch das nächste async/erwartet, dass zeigt, wie die Zukunft wäre Pyramiden zu vermeiden. Vielen Dank an alle Antworten, wirklich hilfreich auf meiner Reise! – Werner

2

AKTUALISIERT

Sie können sie alle zur gleichen Zeit nicht kick off und erwarten, dass die Ergebnisse in der Reihenfolge für Sie fragen loggen sein.

let slow = new Promise((resolve) => { 
 
    setTimeout(() => resolve('slow'), 2000) 
 
}) 
 

 
let instant = new Promise((resolve) => { 
 
    // In the future setImmediate can be used here to ensure async execution. For now, setTimeout with 0MS effectively does the same thing. 
 
    setTimeout(() => resolve('instant'), 0) 
 
}) 
 

 
let quick = new Promise((resolve) => { 
 
    setTimeout(() => resolve('quick'), 1000) 
 
}) 
 

 
Promise.all([slow, instant, quick]).then(function(results) { 
 
    for(let result of results) { 
 
    console.log(result) 
 
    } 
 
    console.log('finished') 
 
}, function(err) { 
 
    console.error(err) 
 
})

Diese Zeitpläne sie alle druckt dann in der richtigen Reihenfolge nach Abschluss: Man könnte etwa wie folgt tun.

+2

Das stimmt nicht. Die Reihenfolge, die Sie daraus erhalten, ist "sofort, schnell, langsam", während OP "schnell, langsam, sofort" wünscht. –

+0

Misread that part, update ... :) – cchamberlain

+2

Hinweis: 'Promise.all()' gibt ein Array von Ergebnissen in der Reihenfolge zurück, in der der 'Promise' oder Wert am Index des an 'Promise.all() übergebenen Arrays gesetzt ist ', nicht die Reihenfolge, in der das 'Versprechen' gelöst ist. Siehe ['Promise.all()'] (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all#Description) _ "Das Array von Werten behält die Reihenfolge bei des ursprünglichen iterierbaren Objekts, nicht die Reihenfolge, in der die Versprechen gelöst wurden. "_, [provide.all innerhalb einer forEach-Schleife - alles feuert auf einmal] (http://stackoverflow.com/q/37089515/) – guest271314

2

Teil des Problems ist die Protokollierung geschieht in der setTimeout Methode, und nicht wirklich von der Versprechen Auflösung.

const slow = new Promise((resolve) => { 
 
    setTimeout(() => { 
 
    console.log('slow - from setTimeout'); 
 
    resolve('slow - from resolve'); 
 
    }, 2000, 'slow'); 
 
}); 
 

 
const instant = new Promise((resolve) => { 
 
    console.log('instant - from setTimeout'); 
 
    resolve('instant - from resolve'); 
 
}); 
 

 
const quick = new Promise((resolve) => { 
 
    setTimeout(() => { 
 
    console.log('quick - from setTimeout'); 
 
    resolve('quick -from resolve'); 
 
    }, 1000, 'quick'); 
 
}); 
 

 
Promise.all([slow, instant, quick]).then((results) => { 
 
    console.log(results); 
 
    console.log('finished'); 
 
}, (error) => { 
 
    console.log(error); 
 
});

im Wert der Methode resolve Passing wird alles in der Promise.all zurück. Die Antwort kommt von jedem Versprechen als ein Array zurück, und Sie können diese Antworten durchlaufen, sobald alle abgeschlossen sind.

+1

Solide Antwort. Dies veranschaulicht den Unterschied zwischen der Protokollierung des Ergebnisses in der Zusage und der Rückgabe des Ergebnisses zur Verwendung in "Promise.all". – Ajedi32

+0

Danke @ Ajedi32 - müssen Sie daran denken, die Snippet-Funktion zu verwenden. super einfach. –

+0

Ja, ich wusste nicht, dass es jetzt die Ergebnisse von 'console.log' anzeigt. Das ist eine sehr nützliche Funktion für reine JavaScript-Fragen wie diese. – Ajedi32

-1

Promise.all() ist nicht erforderlich, um das erwartete Ergebnis zurückzugeben.

Sie Funktion nutzen können, ruft die Promise Konstruktor zurückzukehren, den Wert übergeben, die an console.log() zu resolve() oder reject() übergeben werden sollen. Schieben Sie den Wert auf ein Array. Verwenden Sie , um den zurückgegebenen Wert Promise zu verarbeiten, den zurückgegebenen Wert an das Array zu senden und das Array an den Rückruffunktionsparameter bei der nächsten in der Kette zurückzugeben. Greifen Sie auf die verketteten Array Promise Werte zuletzt .then() in der Kette.

let results = []; 
 

 
let pace = value => {console.log(value); results.push(value); return results}; 
 

 
let slow =() => new Promise((resolve) => { 
 
    setTimeout((value) => { 
 
    resolve(value); 
 
    }, 2000, "slow"); 
 
}); 
 

 
let instant =() => new Promise((resolve) => { 
 
    resolve("instant"); 
 
}); 
 

 
let quick =() => new Promise((resolve) => { 
 
    setTimeout((value) => { 
 
    resolve(value); 
 
    }, 1000, "quick"); 
 
}); 
 

 
slow().then(pace) 
 
.then(instant).then(pace) 
 
.then(quick).then(pace) 
 
.then(res => console.log("finished, results:", res)) 
 
.catch(error => console.log(error));

+0

Code funktioniert jetzt, erfüllt aber immer noch nicht die Anforderung des OP, nicht zu warten, bis das erste Versprechen gelöst ist, bevor das nächste Versprechen gegeben wird. – Ajedi32

+0

@ Ajedi32 _ "erfüllt aber immer noch nicht die Anforderung des OP, nicht zu warten, bis das erste Versprechen gelöst ist, bevor das nächste begonnen wird" _ Was meinst du? Wo wird die nächste Funktion aufgerufen, bevor die aktuelle Funktion aufgerufen wird? – guest271314

+0

Das OP sagt "Was ich hier möchte ist, alle Promises async gleichzeitig zu starten." Diese Methode erfüllt diese Anforderung nicht, da sie nicht "schnell" startet, bevor "langsam" bereits abgeschlossen ist. Das Ergebnis ist, dass Sie insgesamt 3000ms für asynchrone Vorgänge ausgeben anstatt für 2000. – Ajedi32

-1

Zunächst einmal beginnt Ihr Code bereits alle drei Versprechen bei der "gleichzeitig". Sie loggen sich auch "fertig" korrekt ein. Von dem, was ich von der Frage verstehe, möchten Sie behandeln die Ergebnisse der Versprechen in sequenzieller Reihenfolge, aber haben sie parallel ausgeführt.

let slow = new Promise((resolve) => { 
 
    setTimeout(function() 
 
    { 
 
    resolve(); 
 
    }, 2000); 
 
}); 
 

 
let instant = new Promise((resolve) => { 
 
    resolve(); 
 
}); 
 

 
let quick = new Promise((resolve) => { 
 
    setTimeout(function() 
 
    { 
 
    resolve(); 
 
    }, 1000); 
 
}); 
 

 
instant.then(function(results) { 
 
    console.log("instant"); 
 
}).then(function(){return quick;}).then(function(results) { 
 
    console.log("quick"); 
 
}).then(function(){return slow;}).then(function(results) { 
 
    console.log("slow"); 
 
}).then(function(){ return Promise.all([slow, instant, quick]);}).then(function(results) { 
 
    console.log('finished'); 
 
}).catch(function(error) { 
 
    console.log(error); 
 
});

Das garantiert Ihnen die Beschlüsse, um behandeln werde.

Hinweis: In Ihrem Beispiel Sie verwenden setTimeout, die die Handler in der Reihenfolge der Zeit zu nennen ist garantiert, so dass Ihre vorhandenen Code wird bereits „instant“ log, „schnell“, „langsam“, " fertig". Der Code, den ich zur Verfügung gestellt habe, garantiert diese Bestellung für jede Reihe von Versprechen mit unterschiedlichen Lösungszeiten.

+0

Das scheint alles sofort zu lösen, ohne zu warten für jedes der Versprechen zu vervollständigen. – Ajedi32

+0

@ Ajedi32 aktualisiert, um die Verkettung richtig zu handhaben. Nicht sicher, an welche Bibliothek ich dachte, aber ES Promises brauchen Funktionen. – tcooc

+0

Kühl. Vielleicht möchten Sie die Reihenfolge ändern, um sie mit dem zu vergleichen, was der OP in der Frage hatte (langsam, sofort, schnell), anstatt wie jetzt (sofort, schnell, langsam). – Ajedi32

0

Wenn die Anforderungen der Frage sind

"instant" echo nicht/log vor "langsam"

und

aber zugleich, sie müssen alle starten gleichzeitig async.

Sie müssen neu ordnen nur die Elemente innerhalb iterable Objekt Promise.all() geben, oder das resultierende Array Promise.all() gekettet innerhalb .then() anpassen, bevor console.log() auf jedem Element des resultierenden Array aufrufen.

Wenn Anforderung ist

„Wie für ein anderes Versprechen warten?"

oder

"Wie garantiere ich Auflösung Reihenfolge mehrerer Versprechen?"

this Answer sehen.


Promise.all gibt ein Array von Werten von allen Versprechungen in der iterables Objekt, das übergeben wurde. Das Array von Werten behält die Reihenfolge des ursprünglichen iterierbaren Objekts bei, nicht die Reihenfolge, in der die Versprechen aufgelöst wurden. Wenn etwas im iterablen Array übergeben wurde, wird es nicht in Versprechen umgewandelt, sondern in Promise.resolve.

let slow = new Promise((resolve) => { 
 
    setTimeout(function(value) { 
 
\t resolve(value); \t 
 
    }, 2000, "slow"); 
 
}); 
 

 
let instant = new Promise((resolve) => { 
 
\t resolve("instant"); \t 
 
}); 
 

 
let quick = new Promise((resolve) => { 
 
    setTimeout(function(value) { 
 
\t resolve(value); \t 
 
    }, 1000, "quick"); 
 
}); 
 

 
Promise.all([slow, instant, quick]).then(function(results) { 
 
    console.log("finished"); 
 
    console.log(results.join("\n")) 
 
}, function(error) { 
 
    console.log(error); \t 
 
});

Verwandte Themen