Asinkron untuk siklus dalam JavaScript

87

Saya membutuhkan loop yang menunggu panggilan async sebelum melanjutkan. Sesuatu seperti:

for ( /* ... */ ) {

  someFunction(param1, praram2, function(result) {

    // Okay, for cycle could continue

  })

}

alert("For cycle ended");

Bagaimana saya bisa melakukan ini? Apakah kamu punya ide

Menikahlah dengan Hoffser
sumber
128
Wow ( /* ... */ )terlihat seperti monster dan aku ketakutan sekarang :(
Pointy

Jawaban:

182

Anda tidak dapat menggabungkan sinkron dan asinkron dalam JavaScript jika Anda memblokir skrip, Anda memblokir Browser.

Anda harus mengikuti acara lengkap yang digerakkan di sini, untungnya kami dapat menyembunyikan hal-hal buruk itu.

EDIT: Memperbarui kode.

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

Ini akan memberi kita asinkron loop, tentu saja Anda dapat memodifikasinya lebih jauh untuk mengambil misalnya fungsi untuk memeriksa kondisi loop, dll.

Sekarang untuk ujian:

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

Dan hasilnya:

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
sumber
28
mungkin saya melewatkan sesuatu, tetapi saya tidak mengerti bagaimana ini sebenarnya asinkron. Apakah Anda tidak membutuhkan setTimeout atau sesuatu? Saya mencoba kode Anda, mengeluarkan console.log, dan menaikkan jumlah banyak dan itu hanya membekukan browser.
rocketsarefast
Saya bingung apa loop.break()yang harus saya lakukan? Hanya cara untuk memaksa jika Anda mau?
whitfin
2
seperti yang dikatakan rocketsarefast di atas, jawaban ini tidak asinkron dan karena itu sepenuhnya salah!
kofifus
seperti loop.next, loop.break harus dibuat no-op jika done benar: `break: function () {if (! done) {done = true; callback (); }} `
db-inf
4
Maaf, harus memberi suara negatif karena ini sebenarnya tidak asinkron.
Rikaelus
44

Saya menyederhanakan ini:

FUNGSI:

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

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

PEMAKAIAN:

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

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

wilsonpage
sumber
+1 Saya melakukan sesuatu yang mirip dengan ini, tetapi saya meletakkan bagian setTimeout di fungsi perpustakaan.
rocketsarefast
Bukankah pada dasarnya adalah rekursi?
Pavel
7

Alternatif yang lebih bersih dari saran @Ivo adalah Asynchronous Method Queue , dengan asumsi Anda hanya perlu membuat satu panggilan async untuk koleksi.

(Lihat posting ini oleh Dustin Diaz untuk penjelasan lebih detail)

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

Anda cukup membuat instance baru Queue, menambahkan callback yang Anda butuhkan, lalu membersihkan antrean dengan respons asinkron.

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

Manfaat tambahan dari pola ini adalah Anda dapat menambahkan beberapa fungsi ke antrean, bukan hanya satu.

Jika Anda memiliki objek yang berisi fungsi iterator, Anda dapat menambahkan dukungan untuk antrian ini di belakang layar dan menulis kode yang terlihat sinkron, tetapi tidak:

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

cukup tulis eachuntuk meletakkan fungsi anonim ke dalam antrean alih-alih langsung mengeksekusinya, lalu hapus antrean saat panggilan asinkron Anda selesai. Ini adalah pola desain yang sangat sederhana dan kuat.

PS Jika Anda menggunakan jQuery, Anda sudah memiliki antrian metode async yang Anda inginkan yang disebut jQuery.Deferred .

Adam Lassek
sumber
1
Nah, jika memahami pertanyaannya dengan benar, ini tidak akan menghasilkan perilaku yang diinginkan, tampaknya dia ingin melakukan beberapa panggilan balik di someFunctionmana menunda sisa loop, pola Anda menyiapkan daftar fungsi yang akan dieksekusi secara berurutan dan semua akan menerima hasil dari satu pemanggilan fungsi lainnya. Itu pola yang bagus tapi saya rasa itu tidak cocok dengan pertanyaan yang dimaksud.
Ivo Wetzel
@Ivo Tanpa informasi lebih lanjut, kami tidak akan tahu pasti, tetapi berbicara secara umum, menurut saya itu adalah desain yang buruk untuk membuat kode sinkron menunggu operasi asinkron sebelum melanjutkan; dalam setiap kasus saya telah mencobanya, itu menghasilkan kelambatan UI yang terlihat karena JS menjadi single-threaded. Jika operasi berlangsung terlalu lama, Anda menjalankan risiko skrip Anda dihentikan secara paksa oleh browser.
Adam Lassek
@Ivo juga saya sangat waspada dengan kode yang bergantung setTimeout. Anda berisiko melakukan perilaku yang tidak diinginkan jika kode dijalankan lebih cepat dari yang Anda perkirakan.
Adam Lassek
@Adam Dengan cara apa saya mengambil risiko perilaku yang tidak diinginkan dengan setTimeoutbagaimana jika callback hanya membutuhkan separuh waktu, nah kode dijalankan lebih cepat lagi ... jadi apa gunanya? "Kode" dalam "loop" masih berurutan, jika Anda melakukan beberapa hal di luarnya sebelum panggilan balik lengkap Anda sudah meminta masalah, tapi sekali lagi itu single threaded, saya kesulitan membuat skenario di mana setTimeoutakan merusak sesuatu, tanpa kesalahan desain lebih lanjut.
Ivo Wetzel
Juga, dia meminta modul Node.js seperti ini di pertanyaan lain, saya menyatakan di sana bahwa secara umum ide yang buruk untuk memiliki solusi umum untuk loop "async-sync" seperti itu. Saya lebih suka memilih sesuatu yang sesuai dengan persyaratan yang tepat dari apa pun yang ingin saya capai.
Ivo Wetzel
3

Juga lihatlah perpustakaan caolan / async yang luar biasa ini . forLoop Anda dapat dengan mudah diselesaikan menggunakan mapSeries atau series .

Saya dapat memposting beberapa kode contoh jika contoh Anda memiliki lebih banyak detail di dalamnya.

Mrchief
sumber
2

Kami juga dapat menggunakan bantuan jquery.Deferred. dalam hal ini fungsi asyncLoop akan terlihat seperti ini:

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

fungsi panggilan balik akan terlihat seperti ini:

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

contoh fungsi yang menyelesaikan penangguhan:

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

Saya telah menggunakan "setTimeout (Func, 0);" trik selama sekitar tahun. Berikut adalah beberapa penelitian terbaru yang saya tulis untuk menjelaskan cara mempercepatnya sedikit. Jika Anda hanya ingin jawabannya, lanjutkan ke Langkah 4. Langkah 1 2 dan 3 jelaskan alasan dan mekanismenya;

// 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.  
///////////////////////////////////////////////
rocketsarefast
sumber
2

Diberikan fungsi pekerja asynchronous someFunctionyang akan memanggil kembali fungsi hasil dengan resultargumen yang mengatakan apakah loop harus dilanjutkan atau tidak:

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

Untuk memeriksa apakah akan mengakhiri loop atau tidak, fungsi pekerja someFunctiondapat meneruskan fungsi hasil ke operasi asinkron lainnya. Selain itu, seluruh ekspresi dapat dienkapsulasi menjadi fungsi asinkron dengan mengambil fungsi donesebagai callback.

Sebastian
sumber
1

Jika Anda menyukai jawaban wilsonpage tetapi lebih terbiasa menggunakan sintaks async.js, berikut ini variasinya:

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!');
});

Demo dapat ditemukan di sini - http://jsfiddle.net/NXTv7/8/

jczaplew.dll
sumber
1

Berikut contoh lain yang menurut saya lebih mudah dibaca daripada yang lain, di mana Anda membungkus fungsi asinkron Anda di dalam fungsi yang mengambil donefungsi, indeks loop saat ini, dan hasil (jika ada) dari panggilan asinkron sebelumnya:

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

Setelah done()dipanggil, ini memicu panggilan asinkron berikutnya, lagi-lagi meneruskan fungsi yang telah dilakukan, indeks saat ini, dan hasil sebelumnya. Setelah seluruh loop selesai, loop yang disediakan callbackakan dipanggil.

Berikut cuplikan yang dapat Anda jalankan:

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

Anda dapat menggunakan yang async awaitdiperkenalkan di ES7:

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

Ini hanya berfungsi jika someFunctionmengembalikan Janji!

Jika someFunctiontidak mengembalikan Janji, Anda dapat membuatnya mengembalikan Janji sendiri seperti ini:

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

Kemudian ganti baris ini await someFunction(param1, param2);denganawait asynSomeFunction(param1, param2);

Harap pahami Janji sebelum menulis async awaitkode!

Praveena
sumber
Ini harus memberi Unexpected await inside loop.
Reyraa
@Reyraa itu bukan javascriptmasalah. Peringatan itu muncul dari eslintkonfigurasi Anda . Saya selalu menonaktifkan aturan itu eslintkarena di sebagian besar tempat saya benar-benar perlu menunggu di dalam lingkaran
Praveena
0

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

EDIT:

tautan dari 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;
}

Fungsi ini memungkinkan Anda untuk membuat persen break di loop for menggunakan settings.limit. Properti limit hanyalah sebuah bilangan bulat, tetapi ketika disetel sebagai array.length * 0.1, ini akan membuat settings.limit_callback dipanggil setiap 10%.

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

contoh:

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.dll
sumber
0

Solusi berbasis pustaka janji:

/*
    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.dll
sumber
0

Saya perlu memanggil beberapa Xwaktu fungsi asinkron , setiap iterasi pasti terjadi setelah yang sebelumnya selesai, jadi saya menulis a perpustakaan litte yang dapat digunakan seperti ini:

// 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");
});

Setiap kali fungsi loop yang ditentukan pengguna dipanggil, ia memiliki dua argumen, indeks iterasi dan nilai kembali panggilan sebelumnya.

Ini adalah contoh keluaran:

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