JavaScript'te döngü için eşzamansız


87

Devam etmeden önce eşzamansız çağrı bekleyen bir döngüye ihtiyacım var. Gibi bir şey:

for ( /* ... */ ) {

  someFunction(param1, praram2, function(result) {

    // Okay, for cycle could continue

  })

}

alert("For cycle ended");

Bunu nasıl yapabilirim? Hiç fikrin var mı?


128
Vay ( /* ... */ )canavara benziyor ve şimdi korkuyorum :(
Pointy

Yanıtlar:


182

JavaScript'te eşzamanlı ve eşzamansız olanları karıştıramazsınız, komut dizisini engellerseniz Tarayıcıyı engellersiniz.

Burada tam olay odaklı gitmeniz gerekiyor, neyse ki çirkin şeyleri saklayabiliriz.

DÜZENLEME: Kodu güncellendi.

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

Bu bize bir eşzamansızlık sağlayacaktır loop, tabii ki, döngü koşulunu kontrol etmek için bir işlev vb. Almak için daha da fazla değişiklik yapabilirsiniz.

Şimdi teste geçelim:

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

Ve çıktı:

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

28
belki bir şeyi kaçırıyorum, ama bunun nasıl asenkron olduğunu anlamıyorum. SetTimeout veya başka bir şeye ihtiyacınız yok mu? Kodunuzu denedim, console.log dosyasını çıkardım ve sayımı çok artırdım ve tarayıcıyı donduruyor.
rocketsarefast

Ne loop.break()yapılması gerektiği konusunda kafam karıştı mı? İstersen zorla çıkarmanın bir yolu mu?
whitfin

2
Rocketsarefast'ın yukarıda söylediği gibi, bu cevap asenkron değildir ve bu nedenle tamamen yanlıştır!
kofifus

loop.next gibi, loop.break işlemi tamamlandığında işlemsiz yapılmalıdır: `break: function () {if (! done) {done = true; geri aramak(); }} `
db-inf

4
Üzgünüm, aslında eşzamansız olmadığı için olumsuz oy vermek zorunda kaldım.
Rikaelus

44

Bunu basitleştirdim:

FONKSİYON:

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

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

KULLANIM:

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

ÖRNEK: http://jsfiddle.net/NXTv7/8/


+1 Buna benzer bir şey yaptım ama setTimeout kısmını kütüphane fonksiyonuna koydum.
rocketsarefast

Temelde bir özyineleme değil mi?
Pavel

7

@Ivo'nun önerdiğine daha temiz bir alternatif , koleksiyon için yalnızca bir eşzamansız çağrı yapmanız gerektiğini varsayarak Eşzamansız Yöntem Kuyruğu olacaktır .

( Daha ayrıntılı bir açıklama için Dustin Diaz'ın yazdığı bu gönderiye bakın )

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

Basitçe yeni bir örnek oluşturursunuz Queue, ihtiyacınız olan geri aramaları eklersiniz ve ardından kuyruğu eşzamansız yanıt ile temizlersiniz.

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

Bu kalıbın ek bir avantajı, kuyruğa tek bir işlev yerine birden çok işlev ekleyebilmenizdir.

Yineleyici işlevleri içeren bir nesneniz varsa, bu kuyruk için arka planda destek ekleyebilir ve eşzamanlı görünen ancak olmayan kod yazabilirsiniz:

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

sadece eachanonim işlevi hemen çalıştırmak yerine kuyruğa koymak için yazın ve sonra eşzamansız çağrınız tamamlandığında kuyruğu temizleyin. Bu çok basit ve güçlü bir tasarım modelidir.

PS jQuery kullanıyorsanız, emrinizde jQuery.Deferred adında bir zaman uyumsuz yöntem kuyruğunuz zaten var .


1
Peki soruyu doğru anladıysa, bu istenen davranışı vermeyecektir, görünüşe göre someFunctiondöngünün geri kalanını geciktiren bazı geri aramalar yapmak istiyor , kalıbınız sırayla yürütülecek ve tümü alacak sonuçları bir diğer işlev çağrısı. Bu iyi bir model ama söz konusu soruyla uyuştuğunu sanmıyorum.
Ivo Wetzel

@Ivo Daha fazla bilgi olmadan kesin olarak bilemeyeceğiz, ancak genel olarak konuşursak, senkron kodun devam etmeden önce eşzamanlı olmayan bir işlemi beklemesinin kötü bir tasarım olduğunu düşünüyorum; Denediğim her durumda, JS'nin tek iş parçacıklı olması nedeniyle gözle görülür bir UI gecikmesine neden oldu. İşlem çok uzun sürerse, komut dosyanızın tarayıcı tarafından zorla durdurulması riskini alırsınız.
Adam Lassek

@Ivo ayrıca güvenen koda karşı çok temkinliyim setTimeout. Kod beklediğinizden daha hızlı yürütülürse istenmeyen davranış riskiyle karşı karşıya kalırsınız.
Adam Lassek

@Adam İstenmeyen davranışları hangi şekilde riske atarım? setTimeoutGeri arama süresinin sadece yarısını alırsa, kod tekrar daha hızlı çalışır ... peki ne anlamı var? "Döngü" ndeki "kod" hala sıralı, tam geri aramadan önce onun dışında bazı şeyler yaparsanız zaten sorun için çağırıyorsunuz, ancak sonra yine tek iş parçacıklı, bir setTimeoutdaha fazla yanlış tasarım olmadan bir şeyi bozabilecek bir senaryo .
Ivo Wetzel

Ayrıca, başka bir soruda bunun gibi bir Node.js modülü istedi, bu tür "asenkron senkronizasyon" döngüleri için genel bir çözüme sahip olmanın genel olarak kötü bir fikir olduğunu belirttim. Başarmaya çalıştığım her şeyin tam gerekliliklerine uyan bir şeyi tercih ederim.
Ivo Wetzel

3

Ayrıca bu görkemli caolan / async kitaplığına bakın . Kişisel fordöngü kolayca kullanılarak gerçekleştirilebilir mapSeries veya dizi .

Örneğinizde daha fazla ayrıntı varsa, bazı örnek kodlar gönderebilirim.


2

Ayrıca jquery yardımını da kullanabiliriz. bu durumda asyncLoop işlevi şöyle görünür:

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

geri arama işlevi şöyle görünecektir:

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

ertelenmiş olanı çözen örnek işlev:

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

2

Ben "setTimeout (Func, 0);" yaklaşık yıl boyunca hile. İşte onu nasıl biraz hızlandıracağımı açıklamak için yazdığım son araştırmalar. Sadece cevabı istiyorsanız 4. Adıma atlayın. Adım 1 2 ve 3 mantığı ve işleyişi açıklayın;

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

2

Döngünün devam edip etmeyeceğini belirten someFunctionbir resultbağımsız değişkenle bir sonuç işlevini geri çağıracak zaman uyumsuz bir işçi işlevi verildiğinde :

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

Döngünün sonlandırılıp sonlandırılmayacağını kontrol etmek için, çalışan işlevi someFunctionsonuç işlevini diğer eşzamansız işlemlere iletebilir. Ayrıca, tüm ifade, bir işlevi donegeri çağrı olarak alarak bir eşzamansız işlev içinde kapsüllenebilir .


1

Wilsonpage'in cevabını beğendiyseniz ancak async.js'nin sözdizimini kullanmaya daha alışkınsanız, işte bir varyasyon:

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 burada bulunabilir - http://jsfiddle.net/NXTv7/8/


1

İşte diğerlerinden daha okunabilir olduğunu düşündüğüm başka bir örnek, eşzamansız işlevinizi bir doneişlevi, geçerli döngü dizinini ve önceki eşzamansız çağrının sonucunu (varsa) alan bir işlevin içine sardığınız yer:

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

Bir kez done()çağrıldığında, bir sonraki asenkron çağrıyı tetikler, yine done işlevi, geçerli dizini ve önceki sonucu iletir. Döngünün tamamı tamamlandıktan sonra, sağlanan döngü callbackçağrılacaktır.

İşte çalıştırabileceğiniz bir pasaj:

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
}


1

async awaitES7'de sunulanları kullanabilirsiniz :

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

Bu sadece someFunctionbir Sözü geri veriyorsa işe yarar !

Eğer someFunctionbir Promise dönmüyor, o zaman böyle başınıza bir Promise dönmek yapabilirsiniz:

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

Sonra bu satırı şununla değiştirin await someFunction(param1, param2);:await asynSomeFunction(param1, param2);

Lütfen async awaitkod yazmadan önce Sözleri anlayın !


Bu vermeli Unexpected await inside loop.
Reyraa

@Reyraa bu javascriptsorun değil . Bu uyarı eslintyapılandırmanızdan gelir . Bu kuralı her zaman devre dışı eslint
bırakırım

0

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

DÜZENLE:

github'dan bağlantı: 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;
}

Bu işlev, settings.limit'i kullanarak for döngüsünde bir yüzde kırma oluşturmanıza olanak sağlar. Limit özelliği yalnızca bir tamsayıdır, ancak dizi.length * 0.1 olarak ayarlandığında, bu, settings.limit_callback'in her% 10'da bir çağrılmasını sağlar.

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

örnek:

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

0

Kitaplık temelli bir çözüm:

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

0

Bazı eşzamansız işlev Xzamanlarını çağırmam gerekiyordu , her yineleme bir öncekinden sonra gerçekleşmiş olmalı, bu yüzden şu şekilde kullanılabilecek bir küçük kitaplık yazdım :

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

Kullanıcı tanımlı döngü işlevi her çağrıldığında, iki argümanı vardır, yineleme indeksi ve önceki çağrı dönüş değeri.

Bu bir çıktı örneğidir:

"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"
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.