Asynchron für Zyklus in JavaScript

87

Ich benötige eine Schleife, die auf einen asynchronen Aufruf wartet, bevor ich fortfahre. Etwas wie:

for ( /* ... */ ) {

  someFunction(param1, praram2, function(result) {

    // Okay, for cycle could continue

  })

}

alert("For cycle ended");

Wie könnte ich das machen? Hast du eine Idee?

Heirate Hoffser
quelle
128
Wow ( /* ... */ )sieht aus wie ein Monster und ich habe jetzt Angst :(
Pointy

Antworten:

182

Sie können in JavaScript nicht synchron und asynchron mischen, wenn Sie das Skript blockieren, sondern den Browser blockieren.

Sie müssen hier den ganzen ereignisgesteuerten Weg gehen, zum Glück können wir das hässliche Zeug verstecken.

BEARBEITEN: Der Code wurde aktualisiert.

function asyncLoop(iterations, func, callback) {
    var index = 0;
    var done = false;
    var loop = {
        next: function() {
            if (done) {
                return;
            }

            if (index < iterations) {
                index++;
                func(loop);

            } else {
                done = true;
                callback();
            }
        },

        iteration: function() {
            return index - 1;
        },

        break: function() {
            done = true;
            callback();
        }
    };
    loop.next();
    return loop;
}

Dadurch erhalten wir eine asynchrone Funktion loop. Sie können diese natürlich noch weiter ändern, um beispielsweise eine Funktion zum Überprüfen des Schleifenzustands usw. zu verwenden.

Nun zum Test:

function someFunction(a, b, callback) {
    console.log('Hey doing some stuff!');
    callback();
}

asyncLoop(10, function(loop) {
    someFunction(1, 2, function(result) {

        // log the iteration
        console.log(loop.iteration());

        // Okay, for cycle could continue
        loop.next();
    })},
    function(){console.log('cycle ended')}
);

Und die Ausgabe:

Hey doing some stuff!
0
Hey doing some stuff!
1
Hey doing some stuff!
2
Hey doing some stuff!
3
Hey doing some stuff!
4
Hey doing some stuff!
5
Hey doing some stuff!
6
Hey doing some stuff!
7
Hey doing some stuff!
8
Hey doing some stuff!
9
cycle ended
Ivo Wetzel
quelle
28
Vielleicht fehlt mir etwas, aber ich verstehe nicht, wie asynchron das eigentlich ist. Benötigen Sie kein setTimeout oder so? Ich habe Ihren Code ausprobiert, das console.log herausgenommen und die Anzahl stark erhöht, wodurch der Browser nur eingefroren wird.
Raketen sind schnell
Ich bin verwirrt darüber, was loop.break()ich tun soll. Nur eine Möglichkeit, sich zu verdrängen, wenn Sie wollen?
Whitfin
2
Wie Rocketsarefast oben sagt, ist diese Antwort nicht asynchron und daher völlig falsch!
Kofifus
Wie loop.next sollte loop.break zu einem No-Op gemacht werden, wenn done wahr ist: `break: function () {if (! done) {done = true; zurückrufen(); }} `
db-inf
4
Sorry, musste wegen dieser nicht wirklich asynchron abstimmen.
Rikaelus
44

Ich habe das vereinfacht:

FUNKTION:

var asyncLoop = function(o){
    var i=-1;

    var loop = function(){
        i++;
        if(i==o.length){o.callback(); return;}
        o.functionToLoop(loop, i);
    } 
    loop();//init
}

VERWENDUNG:

asyncLoop({
    length : 5,
    functionToLoop : function(loop, i){
        setTimeout(function(){
            document.write('Iteration ' + i + ' <br>');
            loop();
        },1000);
    },
    callback : function(){
        document.write('All done!');
    }    
});

BEISPIEL: http://jsfiddle.net/NXTv7/8/

wilsonpage
quelle
+1 Ich habe etwas Ähnliches gemacht, aber ich habe den setTimeout-Teil in die Bibliotheksfunktion eingefügt.
Raketen sind schnell
Ist das nicht im Grunde eine Rekursion?
Pavel
7

Eine sauberere Alternative zu dem, was @Ivo vorgeschlagen hat, wäre eine Warteschlange für asynchrone Methoden , vorausgesetzt, Sie müssen nur einen asynchronen Aufruf für die Sammlung ausführen.

( Eine ausführlichere Erklärung finden Sie in diesem Beitrag von Dustin Diaz.)

function Queue() {
  this._methods = [];
  this._response = null;
  this._flushed = false;
}

(function(Q){

  Q.add = function (fn) {
    if (this._flushed) fn(this._response);
    else this._methods.push(fn);
  }

  Q.flush = function (response) {
    if (this._flushed) return;
    this._response = response;
    while (this._methods[0]) {
      this._methods.shift()(response);
    }
    this._flushed = true;
  }

})(Queue.prototype);

Sie erstellen einfach eine neue Instanz von Queue, fügen die erforderlichen Rückrufe hinzu und leeren die Warteschlange mit der asynchronen Antwort.

var queue = new Queue();

queue.add(function(results){
  for (var result in results) {
    // normal loop operation here
  }
});

someFunction(param1, param2, function(results) {
  queue.flush(results);
}

Ein zusätzlicher Vorteil dieses Musters besteht darin, dass Sie der Warteschlange mehrere Funktionen hinzufügen können, anstatt nur eine.

Wenn Sie ein Objekt haben, das Iteratorfunktionen enthält, können Sie diese Warteschlange hinter den Kulissen unterstützen und Code schreiben, der synchron aussieht, aber nicht:

MyClass.each(function(result){ ... })

Schreiben Sie einfach, eachum die anonyme Funktion in die Warteschlange zu stellen, anstatt sie sofort auszuführen, und leeren Sie die Warteschlange, wenn Ihr asynchroner Aufruf abgeschlossen ist. Dies ist ein sehr einfaches und leistungsstarkes Entwurfsmuster.

PS Wenn Sie jQuery verwenden, steht Ihnen bereits eine asynchrone Methodenwarteschlange mit dem Namen jQuery.Deferred zur Verfügung .

Adam Lassek
quelle
1
Nun, wenn Sie die Frage richtig verstanden haben, wird dies nicht das gewünschte Verhalten ergeben. Es scheint, dass sie einige Rückrufe durchführen möchte, bei someFunctiondenen der Rest der Schleife verzögert wird. Ihr Muster erstellt eine Liste von Funktionen, die in der richtigen Reihenfolge ausgeführt werden und alle empfangen die Ergebnisse eines weiteren Funktionsaufrufs. Es ist ein gutes Muster, aber ich denke nicht, dass es zu der fraglichen Frage passt.
Ivo Wetzel
@Ivo Ohne weitere Informationen werden wir es nicht sicher wissen, aber allgemein denke ich, dass es ein schlechtes Design ist, synchronen Code auf einen asynchronen Vorgang warten zu lassen, bevor er fortfährt. In jedem Fall, in dem ich es ausprobiert habe, führte dies zu einer merklichen Verzögerung der Benutzeroberfläche, da JS Single-Threaded war. Wenn der Vorgang zu lange dauert, besteht die Gefahr, dass Ihr Skript vom Browser zwangsweise gestoppt wird.
Adam Lassek
@Ivo auch ich bin sehr vorsichtig mit Code, der sich darauf stützt setTimeout. Sie riskieren unbeabsichtigtes Verhalten, wenn der Code schneller ausgeführt wird als erwartet.
Adam Lassek
@Adam Inwiefern würde ich ein unbeabsichtigtes Verhalten riskieren, setTimeoutwenn der Rückruf nur die Hälfte der Zeit in Anspruch nimmt und der Code wieder schneller ausgeführt wird ... worum geht es also? Der "Code" in der "Schleife" ist immer noch in Ordnung. Wenn Sie vor dem vollständigen Rückruf einige Dinge außerhalb des "Rückrufs" ausführen, rufen Sie bereits nach Problemen, aber andererseits ist es ein Thread, ich habe es schwer, einen zu finden Szenario, in dem setTimeoutetwas kaputt gehen würde, ohne weitere Fehlentwürfe.
Ivo Wetzel
Außerdem hat er in einer anderen Frage nach einem Node.js-Modul wie diesem gefragt. Ich habe dort festgestellt, dass es im Allgemeinen eine schlechte Idee ist, eine generische Lösung für solche "Async-Sync" -Schleifen zu haben. Ich würde lieber etwas wählen, das genau den Anforderungen entspricht, die ich erreichen möchte.
Ivo Wetzel
3

Schauen Sie sich auch diese großartige Bibliothek caolan / async an . Ihre forSchleife kann einfach mit mapSeries oder Serien durchgeführt werden .

Ich könnte einen Beispielcode posten, wenn Ihr Beispiel mehr Details enthält.

Mrchief
quelle
2

Wir können auch die Hilfe von jquery verwenden. In diesem Fall würde die asyncLoop-Funktion folgendermaßen aussehen:

asyncLoop = function(array, callback) {
  var nextElement, thisIteration;
  if (array.length > 0) nextElement = array.pop();
  thisIteration = callback(nextElement);
  $.when(thisIteration).done(function(response) {
    // here we can check value of response in order to break or whatever
    if (array.length > 0) asyncLoop(array, collection, callback);
  });
};

Die Rückruffunktion sieht folgendermaßen aus:

addEntry = function(newEntry) {
  var deferred, duplicateEntry;
  // on the next line we can perform some check, which may cause async response.
  duplicateEntry = someCheckHere();
  if (duplicateEntry === true) {
    deferred = $.Deferred();
    // here we launch some other function (e.g. $.ajax or popup window) 
    // which based on result must call deferred.resolve([opt args - response])
    // when deferred.resolve is called "asyncLoop" will start new iteration
    // example function:
    exampleFunction(duplicateEntry, deferred);
    return deferred;
  } else {
    return someActionIfNotDuplicate();
  }
};

Beispielfunktion, die verzögert auflöst:

function exampleFunction(entry, deffered){
  openModal({
    title: "what should we do with duplicate"
    options: [
       {name:"Replace", action: function(){replace(entry);deffered.resolve(replace:true)}},
       {name: "Keep Existing", action: function(){deffered.resolve(replace:false)}}
    ]
  })
}
Vladyslav Goloshchapov
quelle
2

Ich habe das "setTimeout (Func, 0)" verwendet. Trick für etwa ein Jahr. Hier sind einige aktuelle Forschungsergebnisse, die ich geschrieben habe, um zu erklären, wie man es ein bisschen beschleunigt. Wenn Sie nur die Antwort wünschen, fahren Sie mit Schritt 4 fort. In Schritt 1, 2 und 3 werden die Argumentation und die Mechanik erläutert.

// In Depth Analysis of the setTimeout(Func,0) trick.

//////// setTimeout(Func,0) Step 1 ////////////
// setTimeout and setInterval impose a minimum 
// time limit of about 2 to 10 milliseconds.

  console.log("start");
  var workCounter=0;
  var WorkHard = function()
  {
    if(workCounter>=2000) {console.log("done"); return;}
    workCounter++;
    setTimeout(WorkHard,0);
  };

// this take about 9 seconds
// that works out to be about 4.5ms per iteration
// Now there is a subtle rule here that you can tweak
// This minimum is counted from the time the setTimeout was executed.
// THEREFORE:

  console.log("start");
  var workCounter=0;
  var WorkHard = function()
  {
    if(workCounter>=2000) {console.log("done"); return;}
    setTimeout(WorkHard,0);
    workCounter++;
  };

// This code is slightly faster because we register the setTimeout
// a line of code earlier. Actually, the speed difference is immesurable 
// in this case, but the concept is true. Step 2 shows a measurable example.
///////////////////////////////////////////////


//////// setTimeout(Func,0) Step 2 ////////////
// Here is a measurable example of the concept covered in Step 1.

  var StartWork = function()
  {
    console.log("start");
    var startTime = new Date();
    var workCounter=0;
    var sum=0;
    var WorkHard = function()
    {
      if(workCounter>=2000) 
      {
        var ms = (new Date()).getTime() - startTime.getTime();
        console.log("done: sum=" + sum + " time=" + ms + "ms"); 
        return;
      }
      for(var i=0; i<1500000; i++) {sum++;}
      workCounter++;
      setTimeout(WorkHard,0);
    };
    WorkHard();
  };

// This adds some difficulty to the work instead of just incrementing a number
// This prints "done: sum=3000000000 time=18809ms".
// So it took 18.8 seconds.

  var StartWork = function()
  {
    console.log("start");
    var startTime = new Date();
    var workCounter=0;
    var sum=0;
    var WorkHard = function()
    {
      if(workCounter>=2000) 
      {
        var ms = (new Date()).getTime() - startTime.getTime();
        console.log("done: sum=" + sum + " time=" + ms + "ms"); 
        return;
      }
      setTimeout(WorkHard,0);
      for(var i=0; i<1500000; i++) {sum++;}
      workCounter++;
    };
    WorkHard();
  };

// Now, as we planned, we move the setTimeout to before the difficult part
// This prints: "done: sum=3000000000 time=12680ms"
// So it took 12.6 seconds. With a little math, (18.8-12.6)/2000 = 3.1ms
// We have effectively shaved off 3.1ms of the original 4.5ms of dead time.
// Assuming some of that time may be attributed to function calls and variable 
// instantiations, we have eliminated the wait time imposed by setTimeout.

// LESSON LEARNED: If you want to use the setTimeout(Func,0) trick with high 
// performance in mind, make sure your function takes more than 4.5ms, and set 
// the next timeout at the start of your function, instead of the end.
///////////////////////////////////////////////


//////// setTimeout(Func,0) Step 3 ////////////
// The results of Step 2 are very educational, but it doesn't really tell us how to apply the
// concept to the real world.  Step 2 says "make sure your function takes more than 4.5ms".
// No one makes functions that take 4.5ms. Functions either take a few microseconds, 
// or several seconds, or several minutes. This magic 4.5ms is unattainable.

// To solve the problem, we introduce the concept of "Burn Time".
// Lets assume that you can break up your difficult function into pieces that take 
// a few milliseconds or less to complete. Then the concept of Burn Time says, 
// "crunch several of the individual pieces until we reach 4.5ms, then exit"

// Step 1 shows a function that is asyncronous, but takes 9 seconds to run. In reality
// we could have easilly incremented workCounter 2000 times in under a millisecond.
// So, duh, that should not be made asyncronous, its horrible. But what if you don't know
// how many times you need to increment the number, maybe you need to run the loop 20 times,
// maybe you need to run the loop 2 billion times.

  console.log("start");
  var startTime = new Date();
  var workCounter=0;
  for(var i=0; i<2000000000; i++) // 2 billion
  {
    workCounter++;
  }
  var ms = (new Date()).getTime() - startTime.getTime();
  console.log("done: workCounter=" + workCounter + " time=" + ms + "ms"); 

// prints: "done: workCounter=2000000000 time=7214ms"
// So it took 7.2 seconds. Can we break this up into smaller pieces? Yes.
// I know, this is a retarded example, bear with me.

  console.log("start");
  var startTime = new Date();
  var workCounter=0;
  var each = function()
  {
    workCounter++;
  };
  for(var i=0; i<20000000; i++) // 20 million
  {
    each();
  }
  var ms = (new Date()).getTime() - startTime.getTime();
  console.log("done: workCounter=" + workCounter + " time=" + ms + "ms"); 

// The easiest way is to break it up into 2 billion smaller pieces, each of which take 
// only several picoseconds to run. Ok, actually, I am reducing the number from 2 billion
// to 20 million (100x less).  Just adding a function call increases the complexity of the loop
// 100 fold. Good lesson for some other topic.
// prints: "done: workCounter=20000000 time=7648ms"
// So it took 7.6 seconds, thats a good starting point.
// Now, lets sprinkle in the async part with the burn concept

  console.log("start");
  var startTime = new Date();
  var workCounter=0;
  var index=0;
  var end = 20000000;
  var each = function()
  {
    workCounter++;
  };
  var Work = function()
  {
    var burnTimeout = new Date();
    burnTimeout.setTime(burnTimeout.getTime() + 4.5); // burnTimeout set to 4.5ms in the future
    while((new Date()) < burnTimeout)
    {
      if(index>=end) 
      {
        var ms = (new Date()).getTime() - startTime.getTime();
        console.log("done: workCounter=" + workCounter + " time=" + ms + "ms"); 
        return;
      }
      each();
      index++;
    }
    setTimeout(Work,0);
  };

// prints "done: workCounter=20000000 time=107119ms"
// Sweet Jesus, I increased my 7.6 second function to 107.1 seconds.
// But it does prevent the browser from locking up, So i guess thats a plus.
// Again, the actual objective here is just to increment workCounter, so the overhead of all
// the async garbage is huge in comparison. 
// Anyway, Lets start by taking advice from Step 2 and move the setTimeout above the hard part. 

  console.log("start");
  var startTime = new Date();
  var workCounter=0;
  var index=0;
  var end = 20000000;
  var each = function()
  {
    workCounter++;
  };
  var Work = function()
  {
    if(index>=end) {return;}
    setTimeout(Work,0);
    var burnTimeout = new Date();
    burnTimeout.setTime(burnTimeout.getTime() + 4.5); // burnTimeout set to 4.5ms in the future
    while((new Date()) < burnTimeout)
    {
      if(index>=end) 
      {
        var ms = (new Date()).getTime() - startTime.getTime();
        console.log("done: workCounter=" + workCounter + " time=" + ms + "ms"); 
        return;
      }
      each();
      index++;
    }
  };

// This means we also have to check index right away because the last iteration will have nothing to do
// prints "done: workCounter=20000000 time=52892ms"  
// So, it took 52.8 seconds. Improvement, but way slower than the native 7.6 seconds.
// The Burn Time is the number you tweak to get a nice balance between native loop speed
// and browser responsiveness. Lets change it from 4.5ms to 50ms, because we don't really need faster
// than 50ms gui response.

  console.log("start");
  var startTime = new Date();
  var workCounter=0;
  var index=0;
  var end = 20000000;
  var each = function()
  {
    workCounter++;
  };
  var Work = function()
  {
    if(index>=end) {return;}
    setTimeout(Work,0);
    var burnTimeout = new Date();
    burnTimeout.setTime(burnTimeout.getTime() + 50); // burnTimeout set to 50ms in the future
    while((new Date()) < burnTimeout)
    {
      if(index>=end) 
      {
        var ms = (new Date()).getTime() - startTime.getTime();
        console.log("done: workCounter=" + workCounter + " time=" + ms + "ms"); 
        return;
      }
      each();
      index++;
    }
  };

// prints "done: workCounter=20000000 time=52272ms"
// So it took 52.2 seconds. No real improvement here which proves that the imposed limits of setTimeout
// have been eliminated as long as the burn time is anything over 4.5ms
///////////////////////////////////////////////


//////// setTimeout(Func,0) Step 4 ////////////
// The performance numbers from Step 3 seem pretty grim, but GUI responsiveness is often worth it.
// Here is a short library that embodies these concepts and gives a descent interface.

  var WilkesAsyncBurn = function()
  {
    var Now = function() {return (new Date());};
    var CreateFutureDate = function(milliseconds)
    {
      var t = Now();
      t.setTime(t.getTime() + milliseconds);
      return t;
    };
    var For = function(start, end, eachCallback, finalCallback, msBurnTime)
    {
      var i = start;
      var Each = function()
      {
        if(i==-1) {return;} //always does one last each with nothing to do
        setTimeout(Each,0);
        var burnTimeout = CreateFutureDate(msBurnTime);
        while(Now() < burnTimeout)
        {
          if(i>=end) {i=-1; finalCallback(); return;}
          eachCallback(i);
          i++;
        }
      };
      Each();
    };
    var ForEach = function(array, eachCallback, finalCallback, msBurnTime)
    {
      var i = 0;
      var len = array.length;
      var Each = function()
      {
        if(i==-1) {return;}
        setTimeout(Each,0);
        var burnTimeout = CreateFutureDate(msBurnTime);
        while(Now() < burnTimeout)
        {
          if(i>=len) {i=-1; finalCallback(array); return;}
          eachCallback(i, array[i]);
          i++;
        }
      };
      Each();
    };

    var pub = {};
    pub.For = For;          //eachCallback(index); finalCallback();
    pub.ForEach = ForEach;  //eachCallback(index,value); finalCallback(array);
    WilkesAsyncBurn = pub;
  };

///////////////////////////////////////////////


//////// setTimeout(Func,0) Step 5 ////////////
// Here is an examples of how to use the library from Step 4.

  WilkesAsyncBurn(); // Init the library
  console.log("start");
  var startTime = new Date();
  var workCounter=0;
  var FuncEach = function()
  {
    if(workCounter%1000==0)
    {
      var s = "<div></div>";
      var div = jQuery("*[class~=r1]");
      div.append(s);
    }
    workCounter++;
  };
  var FuncFinal = function()
  {
    var ms = (new Date()).getTime() - startTime.getTime();
    console.log("done: workCounter=" + workCounter + " time=" + ms + "ms"); 
  };
  WilkesAsyncBurn.For(0,2000000,FuncEach,FuncFinal,50);

// prints: "done: workCounter=20000000 time=149303ms"
// Also appends a few thousand divs to the html page, about 20 at a time.
// The browser is responsive the entire time, mission accomplished

// LESSON LEARNED: If your code pieces are super tiny, like incrementing a number, or walking through 
// an array summing the numbers, then just putting it in an "each" function is going to kill you. 
// You can still use the concept here, but your "each" function should also have a for loop in it 
// where you burn a few hundred items manually.  
///////////////////////////////////////////////
Raketen sind schnell
quelle
2

Bei einer asynchronen Worker-Funktion someFunction, die eine Ergebnisfunktion mit einem resultArgument zurückruft, das angibt, ob die Schleife fortgesetzt werden soll oder nicht:

// having:
// function someFunction(param1, praram2, resultfunc))
// function done() { alert("For cycle ended"); }

(function(f){ f(f) })(function(f){
  someFunction("param1", "praram2", function(result){
    if (result)
      f(f); // loop continues
    else
      done(); // loop ends
  });
})

Um zu überprüfen, ob die Schleife beendet werden soll oder nicht, kann die Worker-Funktion someFunctiondie Ergebnisfunktion an andere asynchrone Operationen weiterleiten. Außerdem kann der gesamte Ausdruck in eine asynchrone Funktion eingekapselt werden, indem eine Funktion doneals Rückruf verwendet wird.

Sebastian
quelle
1

Wenn Ihnen die Antwort von wilsonpage gefällt, Sie jedoch eher an die Verwendung der Syntax von async.js gewöhnt sind, finden Sie hier eine Variation:

function asyncEach(iterableList, callback, done) {
  var i = -1,
      length = iterableList.length;

  function loop() {
      i++;
      if (i === length) {
        done(); 
        return;
      }
      callback(iterableList[i], loop);
  } 
  loop();
}


asyncEach(['A', 'B', 'C'], function(item, callback) {
    setTimeout(function(){
    document.write('Iteration ' + item + ' <br>');
    callback();
  }, 1000);
}, function() {
  document.write('All done!');
});

Die Demo finden Sie hier - http://jsfiddle.net/NXTv7/8/

jczaplew
quelle
1

Hier ist ein weiteres Beispiel, das meiner Meinung nach besser lesbar ist als andere, bei dem Sie Ihre asynchrone Funktion in eine Funktion einschließen, die eine doneFunktion, den aktuellen Schleifenindex und das Ergebnis (falls vorhanden) des vorherigen asynchronen Aufrufs enthält:

function (done, i, prevResult) {
   // perform async stuff
   // call "done(result)" in async callback 
   // or after promise resolves
}

Sobald done()es aufgerufen wird, löst es den nächsten asynchronen Aufruf aus und übergibt erneut die Funktion done, den aktuellen Index und das vorherige Ergebnis. Sobald die gesamte Schleife abgeschlossen ist, wird die bereitgestellte Schleife callbackaufgerufen.

Hier ist ein Ausschnitt, den Sie ausführen können:

asyncLoop({
  limit: 25,
  asyncLoopFunction: function(done, i, prevResult) {
    setTimeout(function() {
      console.log("Starting Iteration: ", i);
      console.log("Previous Result: ", prevResult);
      var result = i * 100;
      done(result);
    }, 1000);
  },
  initialArgs: 'Hello',
  callback: function(result) {
    console.log('All Done. Final result: ', result);
  }
});

function asyncLoop(obj) {
  var limit = obj.limit,
    asyncLoopFunction = obj.asyncLoopFunction,
    initialArgs = obj.initialArgs || {},
    callback = obj.callback,
    i = 0;

  function done(result) {
    i++;
    if (i < limit) {
      triggerAsync(result);
    } else {
      callback(result);
    }
  }

  function triggerAsync(prevResult) {
    asyncLoopFunction(done, i, prevResult);
  }

  triggerAsync(initialArgs); // init
}

Shimizu
quelle
1

Sie können async awaitin ES7 eingeführt verwenden:

for ( /* ... */ ) {
    let result = await someFunction(param1, param2);
}
alert("For cycle ended");

Dies funktioniert nur, wenn someFunctionein Versprechen zurückgegeben wird!

Wenn Sie someFunctionein Versprechen nicht zurückgeben, können Sie es selbst dazu bringen, ein Versprechen wie folgt zurückzugeben:

function asyncSomeFunction(param1,praram2) {
  return new Promise((resolve, reject) => {
    someFunction(praram1,praram2,(result)=>{
      resolve(result);
    })
  })
}

Ersetzen Sie dann diese Zeile await someFunction(param1, param2);durchawait asynSomeFunction(param1, param2);

Bitte verstehe Versprechen, bevor du async awaitCode schreibst !

Praveena
quelle
Das sollte geben Unexpected await inside loop.
Reyraa
@ Reyraa das ist kein javascriptProblem. Diese Warnung stammt aus Ihrer eslintKonfiguration. Ich deaktiviere diese Regel immer, eslintweil ich an den meisten Stellen, die ich wirklich brauche, innerhalb der Schleife warten muss
Praveena
0

http://cuzztuts.blogspot.ro/2011/12/js-async-for-very-cool.html

BEARBEITEN:

Link von Github: https://github.com/cuzzea/lib_repo/blob/master/cuzzea/js/functions/core/async_for.js

function async_for_each(object,settings){
var l=object.length;
    settings.limit = settings.limit || Math.round(l/100);
    settings.start = settings.start || 0;
    settings.timeout = settings.timeout || 1;
    for(var i=settings.start;i<l;i++){
        if(i-settings.start>=settings.limit){
            setTimeout(function(){
                settings.start = i;
                async_for_each(object,settings)
            },settings.timeout);
            settings.limit_callback ? settings.limit_callback(i,l) : null;
            return false;
        }else{
            settings.cbk ? settings.cbk(i,object[i]) : null;
        }
    }
    settings.end_cbk?settings.end_cbk():null;
    return true;
}

Mit dieser Funktion können Sie mit settings.limit eine prozentuale Unterbrechung in der for-Schleife erstellen. Die limit-Eigenschaft ist nur eine Ganzzahl, aber wenn sie als array.length * 0.1 festgelegt wird, wird die settings.limit_callback alle 10% aufgerufen.

/*
 * params:
 *  object:         the array to parse
 *  settings_object:
 *      cbk:            function to call whenwhen object is found in array
 *                          params: i,object[i]
 *      limit_calback:  function to call when limit is reached
 *                          params: i, object_length
 *      end_cbk:        function to call when loop is finished
 *                          params: none
 *      limit:          number of iteration before breacking the for loop
 *                          default: object.length/100
 *      timeout:        time until start of the for loop(ms)
 *                          default: 1
 *      start:          the index from where to start the for loop
 *                          default: 0
 */

Beispiel:

var a = [];
a.length = 1000;
async_for_each(a,{
    limit_callback:function(i,l){console.log("loading %s/%s - %s%",i,l,Math.round(i*100/l))}
});
Cuzzea
quelle
0

Eine vielversprechende bibliotheksbasierte Lösung:

/*
    Since this is an open question for JS I have used Kris Kowal's Q promises for the same
*/

var Q = require('q');
/*
    Your LOOP body
    @success is a parameter(s) you might pass
*/
var loopBody = function(success) {
    var d = Q.defer(); /* OR use your favorite promise library like $q in angular */
    /*
        'setTimeout' will ideally be your node-like callback with this signature ... (err, data) {}
        as shown, on success you should resolve 
        on failure you should reject (as always ...) 
    */
    setTimeout(function(err, data) {
        if (!err) {
            d.resolve('success');
        } else {
            d.reject('failure');
        }
    }, 100); //100 ms used for illustration only 
    return d.promise;
};

/*
    function to call your loop body 
*/
function loop(itr, fn) {
    var def = Q.defer();
    if (itr <= 0) {
        def.reject({ status: "un-successful " });
    } else {
        var next = loop.bind(undefined, itr - 1, fn); // 'next' is all there is to this 
        var callback = fn.bind(undefined /*, a, b, c.... */ ); // in case you want to pass some parameters into your loop body
        def.promise = callback().then(def.resolve, next);
    }
    return def.promise;
}
/*
    USAGE: loop(iterations, function(){})
    the second argument has to be thenable (in other words return a promise)
    NOTE: this loop will stop when loop body resolves to a success
    Example: Try to upload file 3 times. HURRAY (if successful) or log failed 
*/

loop(4, loopBody).then(function() {
    //success handler
    console.log('HURRAY')
}, function() {
    //failed 
    console.log('failed');
});
tyskr
quelle
0

Ich musste einige asynchrone Funktionszeiten aufrufen X, jede Iteration muss nach der vorherigen stattgefunden haben, also habe ich eine kleine Bibliothek geschrieben , die wie folgt verwendet werden kann:

// https://codepen.io/anon/pen/MOvxaX?editors=0012
var loop = AsyncLoop(function(iteration, value){
  console.log("Loop called with iteration and value set to: ", iteration, value);

  var random = Math.random()*500;

  if(random < 200)
    return false;

  return new Promise(function(resolve){
    setTimeout(resolve.bind(null, random), random);
  });
})
.finished(function(){
  console.log("Loop has ended");
});

Jedes Mal, wenn eine benutzerdefinierte Schleifenfunktion aufgerufen wird, hat sie zwei Argumente, den Iterationsindex und den vorherigen Rückgabewert des Aufrufs.

Dies ist ein Beispiel für die Ausgabe:

"Loop called with iteration and value set to: " 0 null
"Loop called with iteration and value set to: " 1 496.4137048207333
"Loop called with iteration and value set to: " 2 259.6020382449663
"Loop called with iteration and value set to: " 3 485.5400568702862
"Loop has ended"
Buksy
quelle