webentwicklung-frage-antwort-db.com.de

Wie kann ich eine Reihe von Versprechen in sequentieller Reihenfolge ausführen?

Ich habe eine Reihe von Versprechen, die in aufeinanderfolgender Reihenfolge ausgeführt werden müssen.

var promises = [promise1, promise2, ..., promiseN];

Beim Aufruf von RSVP.all werden sie parallel ausgeführt:

RSVP.all(promises).then(...); 

Aber wie kann ich sie nacheinander ausführen?

Ich kann sie manuell stapeln

RSVP.resolve()
    .then(promise1)
    .then(promise2)
    ...
    .then(promiseN)
    .then(...);

das Problem ist jedoch, dass die Anzahl der Versprechen variiert und die Versprechen vielversprechend sind.

70
jaaksarv

Wenn Sie sie bereits in einem Array haben, werden sie bereits ausgeführt. Wenn Sie ein Versprechen haben, wird es bereits ausgeführt. Dies ist kein Anliegen von Versprechungen (dh sie sind in dieser Hinsicht nicht mit C # Tasks mit der .Start()-Methode vergleichbar). .all führt nichts aus it gibt nur ein Versprechen zurück.

Wenn Sie über eine Reihe versprechender Funktionen verfügen:

var tasks = [fn1, fn2, fn3...];

tasks.reduce(function(cur, next) {
    return cur.then(next);
}, RSVP.resolve()).then(function() {
    //all executed
});

Oder Werte:

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
});
121
Esailija

Mit den asynchronen Funktionen von ECMAScript 2017 würde dies folgendermaßen erfolgen:

async function executeSequentially() {
    const tasks = [fn1, fn2, fn3]

    for (const fn of tasks) {
        await fn()
    }
}

Sie können jetzt BabelJS verwenden, um jetzt async-Funktionen zu verwenden

15
Ujeenator

ES7 Weg 2017.

  <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>

Dadurch werden die angegebenen Funktionen nacheinander (nicht nacheinander) ausgeführt. Der Parameter promises ist ein Array von Funktionen, die Promise zurückgeben.

Plunker-Beispiel mit dem obigen Code: http://plnkr.co/edit/UP0rhD?p=preview

5
allenhwkim

Ein zweiter Versuch einer Antwort, in dem ich erklärender zu sein versuche:

Erstens ein notwendiger Hintergrund aus dem RSVP README :

Der wirklich erstaunliche Teil kommt, wenn Sie ein Versprechen vom ersten Handler zurückgeben ... Dadurch können Sie verschachtelte Callbacks abflachen. Dies ist das Hauptmerkmal von Versprechungen, das ein "Rechtsdriften" in Programmen mit viel asynchronem Code verhindert.

Dies ist genau, wie Sie Versprechen sequentiell machen, indem Sie das spätere Versprechen aus der then des Versprechens zurückgeben, das davor enden sollte.

Es ist hilfreich, sich solche Versprechungen als einen Baum vorzustellen, in dem die Zweige sequentielle Prozesse und die Blätter gleichzeitige Prozesse darstellen.

Der Aufbau eines solchen Versprechenbaums ist analog zu der sehr üblichen Aufgabe, andere Baumarten zu erstellen: Halten Sie einen Zeiger oder einen Verweis auf die Stelle im Baum, an der Sie gerade Zweige hinzufügen, und fügen Sie Dinge iterativ hinzu.

Wie @Esailija in seiner Antwort darauf hinwies, können Sie mit reduce, wenn Sie über ein Array von Funktionen verfügen, die versprechen, dass sie keine Argumente akzeptieren, den Baum für Sie ordentlich aufbauen. Wenn Sie einmal redu für sich selbst implementiert haben, werden Sie verstehen, dass das, was in @ Esailijas Antwort hinter den Kulissen geschieht, darin besteht, einen Verweis auf das aktuelle Versprechen (cur) aufrechtzuerhalten und jedes Versprechen das nächste Versprechen in seiner then zurückzugeben.

Wenn Sie NICHT über ein Nice-Array homogener (in Bezug auf die Argumente, die sie annehmen/zurückgeben) versprechen, Funktionen zurückzugeben, oder wenn Sie eine kompliziertere Struktur als eine einfache lineare Sequenz benötigen, können Sie den Versprechenbaum selbst erstellen, indem Sie aufrechterhalten ein Verweis auf die Position im Versprechenbaum, an der Sie neue Versprechen hinzufügen möchten:

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();

Sie können Kombinationen von gleichzeitigen und sequentiellen Prozessen erstellen, indem Sie RSVP.all verwenden, um mehrere "Blätter" zu einem Versprechen "Zweig" hinzuzufügen. Ein Beispiel dafür ist meine nachgewiesene, zu komplizierte Antwort.

Sie können auch Ember.run.scheduleOnce ('afterRender') verwenden, um sicherzustellen, dass etwas in einem Versprechen gemacht wird, bevor das nächste Versprechen ausgelöst wird - meine heruntergekommene, weil es zu komplizierte Antwort ist auch ein Beispiel dafür.

4

Ich werde diese Antwort hier belassen, weil dies mir geholfen hätte, als ich hierher gesucht wurde, um eine Lösung für mein Problem zu finden.

Das, wonach ich gesucht habe, war im Wesentlichen mapSeries ... und ich bin zufällig über eine Menge von Werten gesichert ... und ich möchte die Ergebnisse ...

FWIW, so weit ich gekommen bin, um anderen zu helfen, in der Zukunft nach ähnlichen Dingen zu suchen ...

(Beachten Sie, dass der Kontext eine Ember-App ist.)

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);
    }
});
0
Julian Leviston

Alles was benötigt wird, ist eine for Schleife :)

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);
  });
}
0
Paweł

Ich hatte ein ähnliches Problem und habe eine rekursive Funktion erstellt, die Funktionen nacheinander ausführt.

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();  
};

Für den Fall, dass Sie die Ausgabe aus diesen Funktionen sammeln müssen:

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([]);
};
0
mrded