it-swarm.com.de

Warten Sie, bis sich das nächste Objekt befindet

Ich habe eine Liste von Objekten. Die Objekte werden an eine zurückgestellte Funktion übergeben. Ich möchte die Funktion mit dem nächsten Objekt erst aufrufen, nachdem der vorherige Anruf aufgelöst wurde. Gibt es eine Möglichkeit, dies zu tun?

angular.forEach(objects, function (object) {
    // wait for this to resolve and after that move to next object
    doSomething(object);
});
15
Razvan

Vor ES2017 und async/await (eine Option in ES2017 finden Sie unten), können Sie .forEach() nicht verwenden, wenn Sie auf ein Versprechen warten möchten, da Versprechen nicht blockieren. Javascript und Versprechen funktionieren einfach nicht so.

  1. Sie können mehrere Versprechen verketten und die Versprechen der Infrastruktur in eine Reihenfolge bringen.

  2. Sie können manuell iterieren und die Iteration erst dann fortsetzen, wenn das vorherige Versprechen abgeschlossen ist.

  3. Sie können eine Bibliothek wie async oder Bluebird verwenden, die sie für Sie in Reihenfolge bringt.

Es gibt viele verschiedene Alternativen, aber .forEach() wird das nicht für Sie tun.


Hier ein Beispiel für die Sequenzierung mit Verkettung von Versprechen mit Winkelversprechen (vorausgesetzt, objects ist ein Array):

objects.reduce(function(p, val) {
    return p.then(function() {
        return doSomething(val);
    });
}, $q.when(true)).then(function(finalResult) {
    // done here
}, function(err) {
    // error here
});

Mit den ES6-Standardversprechen wäre dies:

objects.reduce(function(p, val) {
    return p.then(function() {
        return doSomething(val);
    });
}, Promise.resolve()).then(function(finalResult) {
    // done here
}, function(err) {
    // error here
});

Hier ist ein Beispiel für die manuelle Sequenzierung (vorausgesetzt, objects ist ein Array), obwohl dies nicht den Abschluss der Ausführung meldet oder Fehler wie bei der obigen Option:

function run(objects) {
    var cntr = 0;

    function next() {
        if (cntr < objects.length) {
            doSomething(objects[cntr++]).then(next);
        }
    }
    next();
}

ES2017

In ES2017 können Sie mit der async/wait-Funktion auf die Erfüllung eines Versprechens "warten", bevor Sie die Schleifeniteration fortsetzen, wenn Sie nicht funktionsbasierte Schleifen wie for oder while verwenden:

async function someFunc() {
    for (object of objects) {
        // wait for this to resolve and after that move to next object
        let result = await doSomething(object);
    }
}

Der Code muss in einer async-Funktion enthalten sein. Anschließend können Sie mit await dem Interpreter anweisen, auf die Auflösung des Versprechens zu warten, bevor Sie die Schleife fortsetzen. Beachten Sie, dass dies zwar die Art "blockieren" scheint, die Ereignisschleife jedoch nicht blockiert. Andere Ereignisse in der Ereignisschleife können noch während der await verarbeitet werden.

22
jfriend00

Ja, Sie können angular.forEach verwenden, um dies zu erreichen.

Hier ist ein Beispiel (vorausgesetzt, objects ist ein Array):

// Define the initial promise
var sequence = $q.defer();
sequence.resolve();
sequence = sequence.promise;

angular.forEach(objects, function(val,key){
    sequence = sequence.then(function() {
        return doSomething(val);
    });
});

So können Sie dies mit array.reduce tun, ähnlich der Antwort von @ friend00 (vorausgesetzt, objects ist ein Array):

objects.reduce(function(p, val) {
    // The initial promise object
    if(p.then === undefined) {
        p.resolve(); 
        p = p.promise;
    }
    return p.then(function() {
        return doSomething(val);
    });
}, $q.defer());
8
Bjarni

Überprüfen Sie $ q auf Winkel:

function outerFunction() {

  var defer = $q.defer();
  var promises = [];

  function lastTask(){
      writeSome('finish').then( function(){
          defer.resolve();
      });
  }

  angular.forEach( $scope.testArray, function(value){
      promises.Push(writeSome(value));
  });

  $q.all(promises).then(lastTask);

  return defer;
}
4
bln

Der einfachste Weg ist das Erstellen einer Funktion und das manuelle Durchlaufen aller Objekte im Array, nachdem jedes Versprechen aufgelöst wurde.

var delayedFORLoop = function (array) {
    var defer = $q.defer();

    var loop = function (count) {
        var item = array[count];

        // Example of a promise to wait for
        myService.DoCalculation(item).then(function (response) {

        }).finally(function () {
          // Resolve or continue with loop
            if (count === array.length) {
                defer.resolve();
            } else {
                loop(++count);
            }
        });
    }

    loop(0); // Start loop
    return defer.promise;
}

// To use:
delayedFORLoop(array).then(function(response) {
    // Do something
});

Ein Beispiel ist auch auf meinem GitHub verfügbar: https://github.com/pietervw/Deferred-Angular-FOR-Loop-Example

2
Peter-Pan

Es hat für mich so funktioniert. Ich weiß nicht, ob es ein richtiger Ansatz ist, könnte aber dazu beitragen, Leitungen zu reduzieren 

function myFun(){
     var deffer = $q.defer();
     angular.forEach(array,function(a,i) { 
          Service.method(a.id).then(function(res) { 
               console.log(res); 
               if(i == array.length-1) { 
                      deffer.resolve(res); 
               } 
          }); 
     });
     return deffer.promise;
}

myFun().then(function(res){
     //res here
});
0
ajay-annie

Ich verwende eine einfache Lösung für eine Verbindung zu einem Drucker, die wartet, bis das Versprechen abgelaufen ist, um zum nächsten zu gelangen.

angular.forEach(object, function(data){
    yourFunction(data)
    .then(function (){
        return;
    })
})
0
mat lav

Es könnte jemandem helfen, als ich einige der obigen Lösungen ausprobierte, bevor ich meine eigene fand, die tatsächlich für mich funktionierte (die anderen nicht).

  var sequence;
  objects.forEach(function(item) {
     if(sequence === undefined){
          sequence = doSomethingThatReturnsAPromise(item)
          }else{
          sequence = sequence.then(function(){
               return doSomethingThatReturnsAPromise(item)
                     }); 
                 }
        });
0
Lomithrani