Mam szereg obietnic, które muszą być uruchamiane w kolejności.
var promises = [promise1, promise2, ..., promiseN];
Wywołanie RSVP.all spowoduje ich równoległe wykonanie:
RSVP.all(promises).then(...);
Ale jak mogę je uruchomić po kolei?
Mogę ręcznie układać je w ten sposób
RSVP.resolve()
.then(promise1)
.then(promise2)
...
.then(promiseN)
.then(...);
ale problem polega na tym, że liczba obietnic jest różna, a szereg obietnic jest budowany dynamicznie.
javascript
ember.js
promise
rsvp.js
jaaksarv
źródło
źródło
Odpowiedzi:
Jeśli masz już je w tablicy, to już są wykonywane. Jeśli masz obietnicę, to jest ona już wykonywana. Nie dotyczy to obietnic (tj. Nie są one podobne do C #
Task
pod względem.Start()
metody)..all
nie wykonuje niczego, po prostu zwraca obietnicę.Jeśli masz tablicę funkcji zwracających obietnice:
var tasks = [fn1, fn2, fn3...]; tasks.reduce(function(cur, next) { return cur.then(next); }, RSVP.resolve()).then(function() { //all executed });
Lub wartości:
var idsToDelete = [1,2,3]; idsToDelete.reduce(function(cur, next) { return cur.then(function() { return http.post("/delete.php?id=" + next); }); }, RSVP.resolve()).then(function() { //all executed });
źródło
.then
, w tym przykładzie jest po prostu ignorowana ...W przypadku funkcji asynchronicznych ECMAScript 2017 powinno wyglądać to następująco:
async function executeSequentially() { const tasks = [fn1, fn2, fn3] for (const fn of tasks) { await fn() } }
Możesz teraz używać BabelJS do korzystania z funkcji asynchronicznych
źródło
fn1, fn2, fn3
tutaj były funkcje, np() => yourFunctionReturningAPromise()
. W przeciwieństwie do justyourFunctionReturningAPromise()
. Jest to również powód, dla któregoawait fn()
jest to konieczne zamiast tego po prostuawait fn
. Zobacz więcej w oficjalnych dokumentach . Przepraszamy za komentarz, ale kolejka edycji jest pełna :)Droga ES7 w 2017 roku.
<script> var funcs = [ _ => new Promise(resolve => setTimeout(_ => resolve("1"), 1000)), _ => new Promise(resolve => setTimeout(_ => resolve("2"), 1000)), _ => new Promise(resolve => setTimeout(_ => resolve("3"), 1000)), _ => new Promise(resolve => setTimeout(_ => resolve("4"), 1000)), _ => new Promise(resolve => setTimeout(_ => resolve("5"), 1000)), _ => new Promise(resolve => setTimeout(_ => resolve("6"), 1000)), _ => new Promise(resolve => setTimeout(_ => resolve("7"), 1000)) ]; async function runPromisesInSequence(promises) { for (let promise of promises) { console.log(await promise()); } } </script> <button onClick="runPromisesInSequence(funcs)">Do the thing</button>
Spowoduje to wykonanie podanych funkcji sekwencyjnie (jedna po drugiej), a nie równolegle. Parametr
promises
jest tablicą funkcji, które zwracająPromise
.Przykład Plunkera z powyższym kodem: http://plnkr.co/edit/UP0rhD?p=preview
źródło
Druga próba odpowiedzi, w której staram się wyjaśnić:
Po pierwsze, niezbędne informacje z pliku README RSVP :
Dokładnie w ten sposób składasz obietnice sekwencyjnie, zwracając późniejszą obietnicę z
then
obietnicy, która powinna się zakończyć przed nią.Warto pomyśleć o takim zestawie obietnic jak o drzewie, w którym gałęzie reprezentują procesy sekwencyjne, a liście - procesy współbieżne.
Proces tworzenia takiego drzewa obietnic jest analogiczny do bardzo częstego zadania budowania innych rodzajów drzew: utrzymuj wskaźnik lub odniesienie do miejsca w drzewie, w którym obecnie dodajesz gałęzie, i iteracyjnie dodawaj elementy.
Jak zauważył @Esailija w swojej odpowiedzi, jeśli masz tablicę funkcji zwracających obietnice, które nie przyjmują argumentów, możesz użyć,
reduce
aby starannie zbudować drzewo. Jeśli kiedykolwiek wdrożyłeś redukcję dla siebie, zrozumiesz, że to, co redukuje robi za kulisami w odpowiedzi @ Esailija, to utrzymywanie odniesienia do bieżącej obietnicy (cur
) i zwracanie każdej obietnicy następnejthen
.Jeśli NIE masz ładnej tablicy jednorodnych (w odniesieniu do argumentów, które przyjmują / zwracają) obietnic zwracających funkcje lub jeśli potrzebujesz bardziej skomplikowanej struktury niż prosta sekwencja liniowa, możesz samodzielnie skonstruować drzewo obietnic, zachowując odniesienie do pozycji w drzewie obietnic, w której chcesz dodać nowe obietnice:
var root_promise = current_promise = Ember.Deferred.create(); // you can also just use your first real promise as the root; the advantage of // using an empty one is in the case where the process of BUILDING your tree of // promises is also asynchronous and you need to make sure it is built first // before starting it current_promise = current_promise.then(function(){ return // ...something that returns a promise...; }); current_promise = current_promise.then(function(){ return // ...something that returns a promise...; }); // etc. root_promise.resolve();
Można tworzyć kombinacje procesów współbieżnych i sekwencyjnych, używając RSVP.all do dodawania wielu „liści” do „gałęzi” obietnicy. Moja odpowiedź, na którą przyznaję, że jest zbyt skomplikowana, jest tego przykładem.
Możesz również użyć Ember.run.scheduleOnce ('afterRender'), aby upewnić się, że coś, co zostało zrobione w jednej obietnicy, zostanie renderowane przed uruchomieniem następnej obietnicy - moja odpowiedź, która nie została uznana za zbyt skomplikowaną, również pokazuje tego przykład.
źródło
then
jest pożądany, podałeś wiele dodatkowych informacji, które ukrywają odpowiedź na zadane pytanie.Promise.resolve().then(...).then(...)...
, nie żeby tablica rosła podczas wykonywania obietnic. Oczywiście teraz wszystko jest dyskusyjne.Jeszcze innym podejściem jest zdefiniowanie funkcji sekwencji globalnej w
Promise
prototypie.Promise.prototype.sequence = async (promiseFns) => { for (let promiseFn of promiseFns) { await promiseFn(); } }
Wtedy możesz go używać wszędzie, tak jak
Promise.all()
Przykład
const timeout = async ms => new Promise(resolve => setTimeout(() => { console.log("done", ms); resolve(); }, ms) ); // Executed one after the other await Promise.sequence([() => timeout(1000), () => timeout(500)]); // done: 1000 // done: 500 // Executed in parallel await Promise.all([timeout(1000), timeout(500)]); // done: 500 // done: 1000
Zastrzeżenie: zachowaj ostrożność podczas edycji prototypów!
źródło
Do rozwiązania tego wszystkiego potrzeba
for
pętli :)var promises = [a,b,c]; var chain; for(let i in promises){ if(chain) chain = chain.then(promises[i]); if(!chain) chain = promises[i](); } function a(){ return new Promise((resolve)=>{ setTimeout(function(){ console.log('resolve A'); resolve(); },1000); }); } function b(){ return new Promise((resolve)=>{ setTimeout(function(){ console.log('resolve B'); resolve(); },500); }); } function c(){ return new Promise((resolve)=>{ setTimeout(function(){ console.log('resolve C'); resolve(); },100); }); }
źródło
if(!chain) chain = promises[i]();
ma()
na końcu? Myślę, że w przypadku, gdy łańcuch jest pusty (iteracja 0), chciałoby się po prostu mieć surową obietnicę, a następnie pętla może wstrzyknąć każdą kolejną obietnicę do łańcucha.then()
. Tak więc nie byłoby to możliweif(!chain) chain = promises[i];
? Być może czegoś tu nie zrozumiałem.a,b,c
faktycznie są to funkcje zwracające obietnice, a nie obietnice. Więc powyższe ma sens. Ale jaka jest użyteczność zawijania obietnic w ten sposób?Miałem podobny problem i utworzyłem funkcję rekurencyjną, która uruchamia funkcje po kolei.
var tasks = [fn1, fn2, fn3]; var executeSequentially = function(tasks) { if (tasks && tasks.length > 0) { var task = tasks.shift(); return task().then(function() { return executeSequentially(tasks); }); } return Promise.resolve(); };
Jeśli chcesz zebrać dane wyjściowe z tych funkcji:
var tasks = [fn1, fn2, fn3]; var executeSequentially = function(tasks) { if (tasks && tasks.length > 0) { var task = tasks.shift(); return task().then(function(output) { return executeSequentially(tasks).then(function(outputs) { outputs.push(output); return Promise.resolve(outputs); }); }); } return Promise.resolve([]); };
źródło
export type PromiseFn = () => Promise<any>; export class PromiseSequence { private fns: PromiseFn[] = []; push(fn: PromiseFn) { this.fns.push(fn) } async run() { for (const fn of this.fns) { await fn(); } } }
następnie
const seq = new PromiseSequence(); seq.push(() => Promise.resolve(1)); seq.push(() => Promise.resolve(2)); seq.run();
możliwe jest również przechowywanie tego, co obiecuje zwrot w innej prywatnej zmiennej i przekazywanie jej do wywołań zwrotnych
źródło
To, czego szukałem, to zasadniczo mapSeries i tak się składa, że mapuję zapisywanie zestawu wartości i chcę wyników.
Tak więc, tak daleko, jak mam, aby pomóc innym w szukaniu podobnych rzeczy w przyszłości.
(Zwróć uwagę, że kontekstem jest aplikacja Ember).
App = Ember.Application.create(); App.Router.map(function () { // put your routes here }); App.IndexRoute = Ember.Route.extend({ model: function () { var block1 = Em.Object.create({save: function() { return Em.RSVP.resolve("hello"); }}); var block2 = Em.Object.create({save: function() { return Em.RSVP.resolve("this"); }}); var block3 = Em.Object.create({save: function() { return Em.RSVP.resolve("is in sequence"); }}); var values = [block1, block2, block3]; // want to sequentially iterate over each, use reduce, build an array of results similarly to map... var x = values.reduce(function(memo, current) { var last; if(memo.length < 1) { last = current.save(); } else { last = memo[memo.length - 1]; } return memo.concat(last.then(function(results) { return current.save(); })); }, []); return Ember.RSVP.all(x); } });
źródło