Asynchronous for cycle in JavaScript - javascript

I need a loop that waits for an async call before continuing. Something like:
for ( /* ... */ ) {
someFunction(param1, praram2, function(result) {
// Okay, for cycle could continue
})
}
alert("For cycle ended");
How could I do this? Do you have any ideas?

You can't mix synchronous and asynchronous in JavaScript if you block the script, you block the Browser.
You need to go the full event driven way here, luckily we can hide the ugly stuff away.
EDIT: Updated the code.
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;
}
This will provide us an asynchronous loop, you can of course modify it even further to take for example a function to check the loop condition etc.
Now on to the 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')}
);
And the output:
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

I simplified this:
FUNCTION:
var asyncLoop = function(o){
var i=-1;
var loop = function(){
i++;
if(i==o.length){o.callback(); return;}
o.functionToLoop(loop, i);
}
loop();//init
}
USAGE:
asyncLoop({
length : 5,
functionToLoop : function(loop, i){
setTimeout(function(){
document.write('Iteration ' + i + ' <br>');
loop();
},1000);
},
callback : function(){
document.write('All done!');
}
});
EXAMPLE: http://jsfiddle.net/NXTv7/8/

A cleaner alternative to what #Ivo has suggested would be an Asynchronous Method Queue, assuming that you only need to make one async call for the collection.
(See this post by Dustin Diaz for a more detailed explanation)
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);
You simply create a new instance of Queue, add the callbacks you need, and then flush the queue with the async response.
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);
}
An added benefit of this pattern is that you can add multiple functions to the queue instead of just one.
If you have an object which contains iterator functions, you can add support for this queue behind the scenes and write code which looks synchronous, but isn't:
MyClass.each(function(result){ ... })
simply write each to put the anonymous function into the queue instead of executing it immediately, and then flush the queue when your async call is complete. This is a very simple and powerful design pattern.
P.S. If you're using jQuery, you already have an async method queue at your disposal called jQuery.Deferred.

Also look at this splendid library caolan / async. Your for loop can easily be accomplished using mapSeries or series.
I could post some sample code if your example had more details in it.

We can also use help of jquery.Deferred. in this case asyncLoop function would look like this:
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);
});
};
the callback function will look like this:
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();
}
};
example function that resolves deferred:
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)}}
]
})
}

I have been using the "setTimeout(Func,0);" trick for about year. Here is some recent research i wrote up to explain how to speed it up a bit. If you just want the answer, skip to Step 4. Step 1 2 and 3 explain the reasoning and mechanics;
// 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.
///////////////////////////////////////////////

Given an asynchronous worker function someFunction that will call back a result function with a result argument saying whether or not the loop should continue:
// 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
});
})
In order to check whether or not to end the loop, the worker function someFunction can forward the result function to other asynchronous operations. Also, the whole expression can be encapsulated into an asynchronous function by taking a function done as callback.

If you like wilsonpage's answer but are more accustomed to using async.js's syntax, here is a 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!');
});
Demo can be found here - http://jsfiddle.net/NXTv7/8/

Here's another example which I think is more readable than others, where you wrap your async function inside a function that takes in a done function, the current loop index, and the result (if any) of the previous async call:
function (done, i, prevResult) {
// perform async stuff
// call "done(result)" in async callback
// or after promise resolves
}
Once done() is invoked, it triggers the next async call, again passing in the done function, current index and previous result. Once the entire loop is completed, the provided loop callback will be invoked.
Here's a snippet you can run:
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
}

You can use async await introduced in ES7:
for ( /* ... */ ) {
let result = await someFunction(param1, param2);
}
alert("For cycle ended");
This works only if someFunction is returning a Promise!
If someFunction is not returning a Promise, then you can make it return a Promise by yourself like this:
function asyncSomeFunction(param1,praram2) {
return new Promise((resolve, reject) => {
someFunction(praram1,praram2,(result)=>{
resolve(result);
})
})
}
Then replace this line await someFunction(param1, param2); by await asynSomeFunction(param1, param2);
Please understand Promises before writing async await code!

http://cuzztuts.blogspot.ro/2011/12/js-async-for-very-cool.html
EDIT:
link from 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;
}
This function allows you to to create a percent break in the for loop using settings.limit. The limit property is just a integer, but when set as array.length * 0.1, this will make the settings.limit_callback to be called every 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
*/
exemple:
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))}
});

A promise library based solution:
/*
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');
});

I needed to call some asynchronous function X times, each iteration must have happened after the previous one was done, so I wrote a litte library that can be used like this:
// 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");
});
Each time user defined loop function is called, it has two arguments, iteration index and previous call return value.
This is an example of output:
"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"

Related

For a setTimeout in a for loop, why is necessary to multiply the iterating value(i) to the timer, and upon multiplying its not working as expected [duplicate]

I would like to add a delay/sleep inside a while loop:
I tried it like this:
alert('hi');
for(var start = 1; start < 10; start++) {
setTimeout(function () {
alert('hello');
}, 3000);
}
Only the first scenario is true: after showing alert('hi'), it will be waiting for 3 seconds then alert('hello') will be displayed but then alert('hello') will be repeatedly constantly.
What I would like is that after alert('hello') is shown 3 seconds after alert('hi') then it needs to wait for 3 seconds for the second time alert('hello') and so on.
The setTimeout() function is non-blocking and will return immediately. Therefore your loop will iterate very quickly and it will initiate 3-second timeout triggers one after the other in quick succession. That is why your first alerts pops up after 3 seconds, and all the rest follow in succession without any delay.
You may want to use something like this instead:
var i = 1; // set your counter to 1
function myLoop() { // create a loop function
setTimeout(function() { // call a 3s setTimeout when the loop is called
console.log('hello'); // your code here
i++; // increment the counter
if (i < 10) { // if the counter < 10, call the loop function
myLoop(); // .. again which will trigger another
} // .. setTimeout()
}, 3000)
}
myLoop(); // start the loop
You could also neaten it up, by using a self invoking function, passing the number of iterations as an argument:
(function myLoop(i) {
setTimeout(function() {
console.log('hello'); // your code here
if (--i) myLoop(i); // decrement i and call myLoop again if i > 0
}, 3000)
})(10); // pass the number of iterations as an argument
Since ES7 theres a better way to await a loop:
// Returns a Promise that resolves after "ms" Milliseconds
const timer = ms => new Promise(res => setTimeout(res, ms))
async function load () { // We need to wrap the loop into an async function for this to work
for (var i = 0; i < 3; i++) {
console.log(i);
await timer(3000); // then the created Promise can be awaited
}
}
load();
When the engine reaches the await part, it sets a timeout and halts the execution of the async function. Then when the timeout completes, execution continues at that point. That's quite useful as you can delay (1) nested loops, (2) conditionally, (3) nested functions:
async function task(i) { // 3
await timer(1000);
console.log(`Task ${i} done!`);
}
async function main() {
for(let i = 0; i < 100; i+= 10) {
for(let j = 0; j < 10; j++) { // 1
if(j % 2) { // 2
await task(i + j);
}
}
}
}
main();
function timer(ms) { return new Promise(res => setTimeout(res, ms)); }
Reference on MDN
While ES7 is now supported by NodeJS and modern browsers, you might want to transpile it with BabelJS so that it runs everywhere.
If using ES6, you could use a for loop to achieve this:
for (let i = 1; i < 10; i++) {
setTimeout(function timer() {
console.log("hello world");
}, i * 3000);
}
It declares i for each iteration, meaning the timeout is what it was before + 1000. This way, what is passed to setTimeout is exactly what we want.
Try something like this:
var i = 0, howManyTimes = 10;
function f() {
console.log("hi");
i++;
if (i < howManyTimes) {
setTimeout(f, 3000);
}
}
f();
Another way is to multiply the time to timeout, but note that this is not like sleep. Code after the loop will be executed immediately, only the execution of the callback function is deferred.
for (var start = 1; start < 10; start++)
setTimeout(function () { alert('hello'); }, 3000 * start);
The first timeout will be set to 3000 * 1, the second to 3000 * 2 and so on.
This will work
for (var i = 0; i < 10; i++) {
(function(i) {
setTimeout(function() { console.log(i); }, 100 * i);
})(i);
}
Try this fiddle: https://jsfiddle.net/wgdx8zqq/
I think you need something like this:
var TimedQueue = function(defaultDelay){
this.queue = [];
this.index = 0;
this.defaultDelay = defaultDelay || 3000;
};
TimedQueue.prototype = {
add: function(fn, delay){
this.queue.push({
fn: fn,
delay: delay
});
},
run: function(index){
(index || index === 0) && (this.index = index);
this.next();
},
next: function(){
var self = this
, i = this.index++
, at = this.queue[i]
, next = this.queue[this.index]
if(!at) return;
at.fn();
next && setTimeout(function(){
self.next();
}, next.delay||this.defaultDelay);
},
reset: function(){
this.index = 0;
}
}
Test code:
var now = +new Date();
var x = new TimedQueue(2000);
x.add(function(){
console.log('hey');
console.log(+new Date() - now);
});
x.add(function(){
console.log('ho');
console.log(+new Date() - now);
}, 3000);
x.add(function(){
console.log('bye');
console.log(+new Date() - now);
});
x.run();
Note: using alerts stalls javascript execution till you close the alert.
It might be more code than you asked for, but this is a robust reusable solution.
You can create a sleep function that promisifies setTimeout. This enables you to use async/await to write code without callbacks and the familiar for loop control flow.
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
(async () => {
for (let i = 0; i < 10; i++) {
console.log(i);
await sleep(1000);
}
console.log("done");
})();
In Node, you can use timers/promises to avoid the promisification step (if the feature isn't supported on your older Node version, the above code works just as well):
const {setTimeout: sleep} = require("timers/promises");
// same code as above
Regardless, since JS is single-threaded, it's a good thing that timeouts are asynchronous. If they weren't, the browser wouldn't get a chance to repaint the UI, leading to a frozen interface for the user.
I would probably use setInterval, like this:
var period = 1000; // ms
var endTime = 10000; // ms
var counter = 0;
var sleepyAlert = setInterval(function(){
alert('Hello');
if(counter === endTime){
clearInterval(sleepyAlert);
}
counter += period;
}, period);
In my opinion, the simpler and most elegant way to add a delay in a loop is like this:
names = ['John', 'Ana', 'Mary'];
names.forEach((name, i) => {
setTimeout(() => {
console.log(name);
}, i * 1000); // one sec interval
});
In ES6 (ECMAScript 2015) you can iterate with delay with generator and interval.
Generators, a new feature of ECMAScript 6, are functions that can be
paused and resumed. Calling genFunc does not execute it. Instead, it
returns a so-called generator object that lets us control genFunc’s
execution. genFunc() is initially suspended at the beginning of its
body. The method genObj.next() continues the execution of genFunc,
until the next yield.
(Exploring ES6)
Code example:
let arr = [1, 2, 3, 'b'];
let genObj = genFunc();
let val = genObj.next();
console.log(val.value);
let interval = setInterval(() => {
val = genObj.next();
if (val.done) {
clearInterval(interval);
} else {
console.log(val.value);
}
}, 1000);
function* genFunc() {
for(let item of arr) {
yield item;
}
}
So if you are using ES6, that the most elegant way to achieve loop with delay (for my opinion).
I do this with Bluebird’s Promise.delay and recursion.
function myLoop(i) {
return Promise.delay(1000)
.then(function() {
if (i > 0) {
alert('hello');
return myLoop(i -= 1);
}
});
}
myLoop(3);
<script src="//cdnjs.cloudflare.com/ajax/libs/bluebird/2.9.4/bluebird.min.js"></script>
Dead-simple, one-line solution with an actual async-await delay (no queued setTimeout):
The following (self-executing anonymous) function creates an actual delay between loops, instead of multiple setTimeouts with different timeouts, which might mess up memory.
In each of the 100 loops, it awaits for a new Promise to resolve.
This happens only after setTimeout 'allows' it after 90ms. Until then, code is blocked by the async-await / pending Promise.
(async () => {
for (let i=0; i<100; i++) {
await new Promise((resolve) => {setTimeout(() => {document.write(`${i} `); resolve(true)}, 90)});
}
})()
In ES6 you can do as following:
for (let i = 0; i <= 10; i++){
setTimeout(function () {
console.log(i);
}, i*3000)
}
In ES5 you can do as:
for (var i = 0; i <= 10; i++){
(function(i) {
setTimeout(function () {
console.log(i);
}, i*3000)
})(i);
}
The reason is, let allows you to declare variables that are limited to a scope of a block statement, or expression on which it is used, unlike the var keyword, which defines a variable globally, or locally to an entire function regardless of block scope.
A function-less solution
I am a bit late to the party, but there is a solution without using any functions:
alert('hi');
for(var start = 1; start < 10; start++) {
setTimeout(() => alert('hello'), 3000 * start);
}
Just thought I'd post my two cents here as well. This function runs an iterative loop with a delay. See this jsfiddle. The function is as follows:
function timeout(range, time, callback){
var i = range[0];
callback(i);
Loop();
function Loop(){
setTimeout(function(){
i++;
if (i<range[1]){
callback(i);
Loop();
}
}, time*1000)
}
}
For example:
//This function prints the loop number every second
timeout([0, 5], 1, function(i){
console.log(i);
});
Would be equivalent to:
//This function prints the loop number instantly
for (var i = 0; i<5; i++){
console.log(i);
}
To my knowledge the setTimeout function is called asynchronously. What you can do is wrap the entire loop within an async function and await a Promise that contains the setTimeout as shown:
var looper = async function () {
for (var start = 1; start < 10; start++) {
await new Promise(function (resolve, reject) {
setTimeout(function () {
console.log("iteration: " + start.toString());
resolve(true);
}, 1000);
});
}
return true;
}
And then you call run it like so:
looper().then(function(){
console.log("DONE!")
});
Please take some time to get a good understanding of asynchronous programming.
In addition to the accepted answer from 10 years ago, with more modern Javascript one can use async/await/Promise() or generator function to achieve the correct behavior. (The incorrect behavior suggested in other answers would be setting series of 3 seconds alerts regardless of "accepting" the alert() - or finishing the task at hand)
Using async/await/Promise():
alert('hi');
(async () => {
for(let start = 1; start < 10; start++) {
await new Promise(resolve => setTimeout(() => {
alert('hello');
resolve();
}, 3000));
}
})();
Using a generator function:
alert('hi');
let func;
(func = (function*() {
for(let start = 1; start < 10; start++) {
yield setTimeout(() => {
alert('hello');
func.next();
}, 3000);
}
})()).next();
You can use the RxJS interval operator. interval emits an integer every x seconds, and take specifies the number of times it emits these numbers.
Rx.Observable
.interval(1000)
.take(10)
.subscribe((x) => console.log(x))
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/4.1.0/rx.lite.min.js"></script>
var startIndex = 0;
var data = [1, 2, 3];
var timeout = 1000;
function functionToRun(i, length) {
alert(data[i]);
}
(function forWithDelay(i, length, fn, delay) {
setTimeout(function() {
fn(i, length);
i++;
if (i < length) {
forWithDelay(i, length, fn, delay);
}
}, delay);
})(startIndex, data.length, functionToRun, timeout);
A modified version of Daniel Vassallo's answer, with variables extracted into parameters to make the function more reusable:
First let's define some essential variables:
var startIndex = 0;
var data = [1, 2, 3];
var timeout = 3000;
Next you should define the function you want to run. This will get passed i, the current index of the loop and the length of the loop, in case you need it:
function functionToRun(i, length) {
alert(data[i]);
}
Self-executing version
(function forWithDelay(i, length, fn, delay) {
setTimeout(function () {
fn(i, length);
i++;
if (i < length) {
forWithDelay(i, length, fn, delay);
}
}, delay);
})(startIndex, data.length, functionToRun, timeout);
Functional version
function forWithDelay(i, length, fn, delay) {
setTimeout(function () {
fn(i, length);
i++;
if (i < length) {
forWithDelay(i, length, fn, delay);
}
}, delay);
}
forWithDelay(startIndex, data.length, functionToRun, timeout); // Lets run it
Just try this
var arr = ['A','B','C'];
(function customLoop (arr, i) {
setTimeout(function () {
// Do here what you want to do.......
console.log(arr[i]);
if (--i) {
customLoop(arr, i);
}
}, 2000);
})(arr, arr.length);
Result
A // after 2s
B // after 2s
C // after 2s
This script works for most things
function timer(start) {
setTimeout(function () { //The timer
alert('hello');
}, start*3000); //needs the "start*" or else all the timers will run at 3000ms
}
for(var start = 1; start < 10; start++) {
timer(start);
}
Here is how I created an infinite loop with a delay that breaks on a certain condition:
// Now continuously check the app status until it's completed,
// failed or times out. The isFinished() will throw exception if
// there is a failure.
while (true) {
let status = await this.api.getStatus(appId);
if (isFinished(status)) {
break;
} else {
// Delay before running the next loop iteration:
await new Promise(resolve => setTimeout(resolve, 3000));
}
}
The key here is to create a new Promise that resolves by timeout, and to await for its resolution.
Obviously you need async/await support for that. Works in Node 8.
for common use "forget normal loops" and use this combination of "setInterval" includes "setTimeOut"s: like this (from my real tasks).
function iAsk(lvl){
var i=0;
var intr =setInterval(function(){ // start the loop
i++; // increment it
if(i>lvl){ // check if the end round reached.
clearInterval(intr);
return;
}
setTimeout(function(){
$(".imag").prop("src",pPng); // do first bla bla bla after 50 millisecond
},50);
setTimeout(function(){
// do another bla bla bla after 100 millisecond.
seq[i-1]=(Math.ceil(Math.random()*4)).toString();
$("#hh").after('<br>'+i + ' : rand= '+(Math.ceil(Math.random()*4)).toString()+' > '+seq[i-1]);
$("#d"+seq[i-1]).prop("src",pGif);
var d =document.getElementById('aud');
d.play();
},100);
setTimeout(function(){
// keep adding bla bla bla till you done :)
$("#d"+seq[i-1]).prop("src",pPng);
},900);
},1000); // loop waiting time must be >= 900 (biggest timeOut for inside actions)
}
PS: Understand that the real behavior of (setTimeOut): they all will start in same time "the three bla bla bla will start counting down in the same moment" so make a different timeout to arrange the execution.
PS 2: the example for timing loop, but for a reaction loops you can use events, promise async await ..
let counter =1;
for(let item in items) {
counter++;
setTimeout(()=>{
//your code
},counter*5000); //5Sec delay between each iteration
}
const autoPlayer = (arr = [1, 2, 3, 4, 5]) => {
// Base case:
if (arr.length < 1) return
// Remove the first element from the array.
const item = arr.shift()
// Set timout
setTimeout(() => {
console.log('Hello, world!', item) // Visualisation.
autoPlayer() // Call function again.
}, 1000) // Iterate every second.
}
Hey, I know this post is very old, but this code "loops" and adds a delay to it using a recursive method. I don't think you can 'actually' delay a loop itself from iterating based on reading various comments from other people. Maybe this can help someone out! Basically the function accepts an array (in this example). On each iteration the setTimeout Javascript method is called. The function calls itself again indefinitely when the timer of the setTimeout function expires, but on each call the array becomes smaller until it reaches the base-case. I hope this can help anyone else out.
/*
Use Recursive and setTimeout
call below function will run loop loopFunctionNeedCheck until
conditionCheckAfterRunFn = true, if conditionCheckAfterRunFn == false : delay
reRunAfterMs miliseconds and continue loop
tested code, thanks
*/
function functionRepeatUntilConditionTrue(reRunAfterMs, conditionCheckAfterRunFn,
loopFunctionNeedCheck) {
loopFunctionNeedCheck();
var result = conditionCheckAfterRunFn();
//check after run
if (!result) {
setTimeout(function () {
functionRepeatUntilConditionTrue(reRunAfterMs, conditionCheckAfterRunFn, loopFunctionNeedCheck)
}, reRunAfterMs);
}
else console.log("completed, thanks");
//if you need call a function after completed add code call callback in here
}
//passing-parameters-to-a-callback-function
// From Prototype.js
if (!Function.prototype.bind) { // check if native implementation available
Function.prototype.bind = function () {
var fn = this, args = Array.prototype.slice.call(arguments),
object = args.shift();
return function () {
return fn.apply(object,
args.concat(Array.prototype.slice.call(arguments)));
};
};
}
//test code:
var result = 0;
console.log("---> init result is " + result);
var functionNeedRun = function (step) {
result+=step;
console.log("current result is " + result);
}
var checkResultFunction = function () {
return result==100;
}
//call this function will run loop functionNeedRun and delay 500 miliseconds until result=100
functionRepeatUntilConditionTrue(500, checkResultFunction , functionNeedRun.bind(null, 5));
//result log from console:
/*
---> init result is 0
current result is 5
undefined
current result is 10
current result is 15
current result is 20
current result is 25
current result is 30
current result is 35
current result is 40
current result is 45
current result is 50
current result is 55
current result is 60
current result is 65
current result is 70
current result is 75
current result is 80
current result is 85
current result is 90
current result is 95
current result is 100
completed, thanks
*/
Here is a function that I use for looping over an array:
function loopOnArrayWithDelay(theArray, delayAmount, i, theFunction, onComplete){
if (i < theArray.length && typeof delayAmount == 'number'){
console.log("i "+i);
theFunction(theArray[i], i);
setTimeout(function(){
loopOnArrayWithDelay(theArray, delayAmount, (i+1), theFunction, onComplete)}, delayAmount);
}else{
onComplete(i);
}
}
You use it like this:
loopOnArrayWithDelay(YourArray, 1000, 0, function(e, i){
//Do something with item
}, function(i){
//Do something once loop has completed
}
<!DOCTYPE html>
<html>
<body>
<button onclick="myFunction()">Try it</button>
<p id="demo"></p>
<script>
function myFunction() {
for(var i=0; i<5; i++) {
var sno = i+1;
(function myLoop (i) {
setTimeout(function () {
alert(i); // Do your function here
}, 1000*i);
})(sno);
}
}
</script>
</body>
</html>
Try this...
var icount=0;
for (let i in items) {
icount=icount+1000;
new beginCount(items[i],icount);
}
function beginCount(item,icount){
setTimeout(function () {
new actualFunction(item,icount);
}, icount);
}
function actualFunction(item,icount){
//...runs ever 1 second
console.log(icount);
}

Javascript Fetch Api Data and show each item [duplicate]

I would like to add a delay/sleep inside a while loop:
I tried it like this:
alert('hi');
for(var start = 1; start < 10; start++) {
setTimeout(function () {
alert('hello');
}, 3000);
}
Only the first scenario is true: after showing alert('hi'), it will be waiting for 3 seconds then alert('hello') will be displayed but then alert('hello') will be repeatedly constantly.
What I would like is that after alert('hello') is shown 3 seconds after alert('hi') then it needs to wait for 3 seconds for the second time alert('hello') and so on.
The setTimeout() function is non-blocking and will return immediately. Therefore your loop will iterate very quickly and it will initiate 3-second timeout triggers one after the other in quick succession. That is why your first alerts pops up after 3 seconds, and all the rest follow in succession without any delay.
You may want to use something like this instead:
var i = 1; // set your counter to 1
function myLoop() { // create a loop function
setTimeout(function() { // call a 3s setTimeout when the loop is called
console.log('hello'); // your code here
i++; // increment the counter
if (i < 10) { // if the counter < 10, call the loop function
myLoop(); // .. again which will trigger another
} // .. setTimeout()
}, 3000)
}
myLoop(); // start the loop
You could also neaten it up, by using a self invoking function, passing the number of iterations as an argument:
(function myLoop(i) {
setTimeout(function() {
console.log('hello'); // your code here
if (--i) myLoop(i); // decrement i and call myLoop again if i > 0
}, 3000)
})(10); // pass the number of iterations as an argument
Since ES7 theres a better way to await a loop:
// Returns a Promise that resolves after "ms" Milliseconds
const timer = ms => new Promise(res => setTimeout(res, ms))
async function load () { // We need to wrap the loop into an async function for this to work
for (var i = 0; i < 3; i++) {
console.log(i);
await timer(3000); // then the created Promise can be awaited
}
}
load();
When the engine reaches the await part, it sets a timeout and halts the execution of the async function. Then when the timeout completes, execution continues at that point. That's quite useful as you can delay (1) nested loops, (2) conditionally, (3) nested functions:
async function task(i) { // 3
await timer(1000);
console.log(`Task ${i} done!`);
}
async function main() {
for(let i = 0; i < 100; i+= 10) {
for(let j = 0; j < 10; j++) { // 1
if(j % 2) { // 2
await task(i + j);
}
}
}
}
main();
function timer(ms) { return new Promise(res => setTimeout(res, ms)); }
Reference on MDN
While ES7 is now supported by NodeJS and modern browsers, you might want to transpile it with BabelJS so that it runs everywhere.
If using ES6, you could use a for loop to achieve this:
for (let i = 1; i < 10; i++) {
setTimeout(function timer() {
console.log("hello world");
}, i * 3000);
}
It declares i for each iteration, meaning the timeout is what it was before + 1000. This way, what is passed to setTimeout is exactly what we want.
Try something like this:
var i = 0, howManyTimes = 10;
function f() {
console.log("hi");
i++;
if (i < howManyTimes) {
setTimeout(f, 3000);
}
}
f();
Another way is to multiply the time to timeout, but note that this is not like sleep. Code after the loop will be executed immediately, only the execution of the callback function is deferred.
for (var start = 1; start < 10; start++)
setTimeout(function () { alert('hello'); }, 3000 * start);
The first timeout will be set to 3000 * 1, the second to 3000 * 2 and so on.
This will work
for (var i = 0; i < 10; i++) {
(function(i) {
setTimeout(function() { console.log(i); }, 100 * i);
})(i);
}
Try this fiddle: https://jsfiddle.net/wgdx8zqq/
I think you need something like this:
var TimedQueue = function(defaultDelay){
this.queue = [];
this.index = 0;
this.defaultDelay = defaultDelay || 3000;
};
TimedQueue.prototype = {
add: function(fn, delay){
this.queue.push({
fn: fn,
delay: delay
});
},
run: function(index){
(index || index === 0) && (this.index = index);
this.next();
},
next: function(){
var self = this
, i = this.index++
, at = this.queue[i]
, next = this.queue[this.index]
if(!at) return;
at.fn();
next && setTimeout(function(){
self.next();
}, next.delay||this.defaultDelay);
},
reset: function(){
this.index = 0;
}
}
Test code:
var now = +new Date();
var x = new TimedQueue(2000);
x.add(function(){
console.log('hey');
console.log(+new Date() - now);
});
x.add(function(){
console.log('ho');
console.log(+new Date() - now);
}, 3000);
x.add(function(){
console.log('bye');
console.log(+new Date() - now);
});
x.run();
Note: using alerts stalls javascript execution till you close the alert.
It might be more code than you asked for, but this is a robust reusable solution.
You can create a sleep function that promisifies setTimeout. This enables you to use async/await to write code without callbacks and the familiar for loop control flow.
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
(async () => {
for (let i = 0; i < 10; i++) {
console.log(i);
await sleep(1000);
}
console.log("done");
})();
In Node, you can use timers/promises to avoid the promisification step (if the feature isn't supported on your older Node version, the above code works just as well):
const {setTimeout: sleep} = require("timers/promises");
// same code as above
Regardless, since JS is single-threaded, it's a good thing that timeouts are asynchronous. If they weren't, the browser wouldn't get a chance to repaint the UI, leading to a frozen interface for the user.
I would probably use setInterval, like this:
var period = 1000; // ms
var endTime = 10000; // ms
var counter = 0;
var sleepyAlert = setInterval(function(){
alert('Hello');
if(counter === endTime){
clearInterval(sleepyAlert);
}
counter += period;
}, period);
In my opinion, the simpler and most elegant way to add a delay in a loop is like this:
names = ['John', 'Ana', 'Mary'];
names.forEach((name, i) => {
setTimeout(() => {
console.log(name);
}, i * 1000); // one sec interval
});
In ES6 (ECMAScript 2015) you can iterate with delay with generator and interval.
Generators, a new feature of ECMAScript 6, are functions that can be
paused and resumed. Calling genFunc does not execute it. Instead, it
returns a so-called generator object that lets us control genFunc’s
execution. genFunc() is initially suspended at the beginning of its
body. The method genObj.next() continues the execution of genFunc,
until the next yield.
(Exploring ES6)
Code example:
let arr = [1, 2, 3, 'b'];
let genObj = genFunc();
let val = genObj.next();
console.log(val.value);
let interval = setInterval(() => {
val = genObj.next();
if (val.done) {
clearInterval(interval);
} else {
console.log(val.value);
}
}, 1000);
function* genFunc() {
for(let item of arr) {
yield item;
}
}
So if you are using ES6, that the most elegant way to achieve loop with delay (for my opinion).
I do this with Bluebird’s Promise.delay and recursion.
function myLoop(i) {
return Promise.delay(1000)
.then(function() {
if (i > 0) {
alert('hello');
return myLoop(i -= 1);
}
});
}
myLoop(3);
<script src="//cdnjs.cloudflare.com/ajax/libs/bluebird/2.9.4/bluebird.min.js"></script>
Dead-simple, one-line solution with an actual async-await delay (no queued setTimeout):
The following (self-executing anonymous) function creates an actual delay between loops, instead of multiple setTimeouts with different timeouts, which might mess up memory.
In each of the 100 loops, it awaits for a new Promise to resolve.
This happens only after setTimeout 'allows' it after 90ms. Until then, code is blocked by the async-await / pending Promise.
(async () => {
for (let i=0; i<100; i++) {
await new Promise((resolve) => {setTimeout(() => {document.write(`${i} `); resolve(true)}, 90)});
}
})()
In ES6 you can do as following:
for (let i = 0; i <= 10; i++){
setTimeout(function () {
console.log(i);
}, i*3000)
}
In ES5 you can do as:
for (var i = 0; i <= 10; i++){
(function(i) {
setTimeout(function () {
console.log(i);
}, i*3000)
})(i);
}
The reason is, let allows you to declare variables that are limited to a scope of a block statement, or expression on which it is used, unlike the var keyword, which defines a variable globally, or locally to an entire function regardless of block scope.
A function-less solution
I am a bit late to the party, but there is a solution without using any functions:
alert('hi');
for(var start = 1; start < 10; start++) {
setTimeout(() => alert('hello'), 3000 * start);
}
Just thought I'd post my two cents here as well. This function runs an iterative loop with a delay. See this jsfiddle. The function is as follows:
function timeout(range, time, callback){
var i = range[0];
callback(i);
Loop();
function Loop(){
setTimeout(function(){
i++;
if (i<range[1]){
callback(i);
Loop();
}
}, time*1000)
}
}
For example:
//This function prints the loop number every second
timeout([0, 5], 1, function(i){
console.log(i);
});
Would be equivalent to:
//This function prints the loop number instantly
for (var i = 0; i<5; i++){
console.log(i);
}
To my knowledge the setTimeout function is called asynchronously. What you can do is wrap the entire loop within an async function and await a Promise that contains the setTimeout as shown:
var looper = async function () {
for (var start = 1; start < 10; start++) {
await new Promise(function (resolve, reject) {
setTimeout(function () {
console.log("iteration: " + start.toString());
resolve(true);
}, 1000);
});
}
return true;
}
And then you call run it like so:
looper().then(function(){
console.log("DONE!")
});
Please take some time to get a good understanding of asynchronous programming.
In addition to the accepted answer from 10 years ago, with more modern Javascript one can use async/await/Promise() or generator function to achieve the correct behavior. (The incorrect behavior suggested in other answers would be setting series of 3 seconds alerts regardless of "accepting" the alert() - or finishing the task at hand)
Using async/await/Promise():
alert('hi');
(async () => {
for(let start = 1; start < 10; start++) {
await new Promise(resolve => setTimeout(() => {
alert('hello');
resolve();
}, 3000));
}
})();
Using a generator function:
alert('hi');
let func;
(func = (function*() {
for(let start = 1; start < 10; start++) {
yield setTimeout(() => {
alert('hello');
func.next();
}, 3000);
}
})()).next();
You can use the RxJS interval operator. interval emits an integer every x seconds, and take specifies the number of times it emits these numbers.
Rx.Observable
.interval(1000)
.take(10)
.subscribe((x) => console.log(x))
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/4.1.0/rx.lite.min.js"></script>
var startIndex = 0;
var data = [1, 2, 3];
var timeout = 1000;
function functionToRun(i, length) {
alert(data[i]);
}
(function forWithDelay(i, length, fn, delay) {
setTimeout(function() {
fn(i, length);
i++;
if (i < length) {
forWithDelay(i, length, fn, delay);
}
}, delay);
})(startIndex, data.length, functionToRun, timeout);
A modified version of Daniel Vassallo's answer, with variables extracted into parameters to make the function more reusable:
First let's define some essential variables:
var startIndex = 0;
var data = [1, 2, 3];
var timeout = 3000;
Next you should define the function you want to run. This will get passed i, the current index of the loop and the length of the loop, in case you need it:
function functionToRun(i, length) {
alert(data[i]);
}
Self-executing version
(function forWithDelay(i, length, fn, delay) {
setTimeout(function () {
fn(i, length);
i++;
if (i < length) {
forWithDelay(i, length, fn, delay);
}
}, delay);
})(startIndex, data.length, functionToRun, timeout);
Functional version
function forWithDelay(i, length, fn, delay) {
setTimeout(function () {
fn(i, length);
i++;
if (i < length) {
forWithDelay(i, length, fn, delay);
}
}, delay);
}
forWithDelay(startIndex, data.length, functionToRun, timeout); // Lets run it
Just try this
var arr = ['A','B','C'];
(function customLoop (arr, i) {
setTimeout(function () {
// Do here what you want to do.......
console.log(arr[i]);
if (--i) {
customLoop(arr, i);
}
}, 2000);
})(arr, arr.length);
Result
A // after 2s
B // after 2s
C // after 2s
This script works for most things
function timer(start) {
setTimeout(function () { //The timer
alert('hello');
}, start*3000); //needs the "start*" or else all the timers will run at 3000ms
}
for(var start = 1; start < 10; start++) {
timer(start);
}
Here is how I created an infinite loop with a delay that breaks on a certain condition:
// Now continuously check the app status until it's completed,
// failed or times out. The isFinished() will throw exception if
// there is a failure.
while (true) {
let status = await this.api.getStatus(appId);
if (isFinished(status)) {
break;
} else {
// Delay before running the next loop iteration:
await new Promise(resolve => setTimeout(resolve, 3000));
}
}
The key here is to create a new Promise that resolves by timeout, and to await for its resolution.
Obviously you need async/await support for that. Works in Node 8.
for common use "forget normal loops" and use this combination of "setInterval" includes "setTimeOut"s: like this (from my real tasks).
function iAsk(lvl){
var i=0;
var intr =setInterval(function(){ // start the loop
i++; // increment it
if(i>lvl){ // check if the end round reached.
clearInterval(intr);
return;
}
setTimeout(function(){
$(".imag").prop("src",pPng); // do first bla bla bla after 50 millisecond
},50);
setTimeout(function(){
// do another bla bla bla after 100 millisecond.
seq[i-1]=(Math.ceil(Math.random()*4)).toString();
$("#hh").after('<br>'+i + ' : rand= '+(Math.ceil(Math.random()*4)).toString()+' > '+seq[i-1]);
$("#d"+seq[i-1]).prop("src",pGif);
var d =document.getElementById('aud');
d.play();
},100);
setTimeout(function(){
// keep adding bla bla bla till you done :)
$("#d"+seq[i-1]).prop("src",pPng);
},900);
},1000); // loop waiting time must be >= 900 (biggest timeOut for inside actions)
}
PS: Understand that the real behavior of (setTimeOut): they all will start in same time "the three bla bla bla will start counting down in the same moment" so make a different timeout to arrange the execution.
PS 2: the example for timing loop, but for a reaction loops you can use events, promise async await ..
let counter =1;
for(let item in items) {
counter++;
setTimeout(()=>{
//your code
},counter*5000); //5Sec delay between each iteration
}
const autoPlayer = (arr = [1, 2, 3, 4, 5]) => {
// Base case:
if (arr.length < 1) return
// Remove the first element from the array.
const item = arr.shift()
// Set timout
setTimeout(() => {
console.log('Hello, world!', item) // Visualisation.
autoPlayer() // Call function again.
}, 1000) // Iterate every second.
}
Hey, I know this post is very old, but this code "loops" and adds a delay to it using a recursive method. I don't think you can 'actually' delay a loop itself from iterating based on reading various comments from other people. Maybe this can help someone out! Basically the function accepts an array (in this example). On each iteration the setTimeout Javascript method is called. The function calls itself again indefinitely when the timer of the setTimeout function expires, but on each call the array becomes smaller until it reaches the base-case. I hope this can help anyone else out.
/*
Use Recursive and setTimeout
call below function will run loop loopFunctionNeedCheck until
conditionCheckAfterRunFn = true, if conditionCheckAfterRunFn == false : delay
reRunAfterMs miliseconds and continue loop
tested code, thanks
*/
function functionRepeatUntilConditionTrue(reRunAfterMs, conditionCheckAfterRunFn,
loopFunctionNeedCheck) {
loopFunctionNeedCheck();
var result = conditionCheckAfterRunFn();
//check after run
if (!result) {
setTimeout(function () {
functionRepeatUntilConditionTrue(reRunAfterMs, conditionCheckAfterRunFn, loopFunctionNeedCheck)
}, reRunAfterMs);
}
else console.log("completed, thanks");
//if you need call a function after completed add code call callback in here
}
//passing-parameters-to-a-callback-function
// From Prototype.js
if (!Function.prototype.bind) { // check if native implementation available
Function.prototype.bind = function () {
var fn = this, args = Array.prototype.slice.call(arguments),
object = args.shift();
return function () {
return fn.apply(object,
args.concat(Array.prototype.slice.call(arguments)));
};
};
}
//test code:
var result = 0;
console.log("---> init result is " + result);
var functionNeedRun = function (step) {
result+=step;
console.log("current result is " + result);
}
var checkResultFunction = function () {
return result==100;
}
//call this function will run loop functionNeedRun and delay 500 miliseconds until result=100
functionRepeatUntilConditionTrue(500, checkResultFunction , functionNeedRun.bind(null, 5));
//result log from console:
/*
---> init result is 0
current result is 5
undefined
current result is 10
current result is 15
current result is 20
current result is 25
current result is 30
current result is 35
current result is 40
current result is 45
current result is 50
current result is 55
current result is 60
current result is 65
current result is 70
current result is 75
current result is 80
current result is 85
current result is 90
current result is 95
current result is 100
completed, thanks
*/
Here is a function that I use for looping over an array:
function loopOnArrayWithDelay(theArray, delayAmount, i, theFunction, onComplete){
if (i < theArray.length && typeof delayAmount == 'number'){
console.log("i "+i);
theFunction(theArray[i], i);
setTimeout(function(){
loopOnArrayWithDelay(theArray, delayAmount, (i+1), theFunction, onComplete)}, delayAmount);
}else{
onComplete(i);
}
}
You use it like this:
loopOnArrayWithDelay(YourArray, 1000, 0, function(e, i){
//Do something with item
}, function(i){
//Do something once loop has completed
}
<!DOCTYPE html>
<html>
<body>
<button onclick="myFunction()">Try it</button>
<p id="demo"></p>
<script>
function myFunction() {
for(var i=0; i<5; i++) {
var sno = i+1;
(function myLoop (i) {
setTimeout(function () {
alert(i); // Do your function here
}, 1000*i);
})(sno);
}
}
</script>
</body>
</html>
Try this...
var icount=0;
for (let i in items) {
icount=icount+1000;
new beginCount(items[i],icount);
}
function beginCount(item,icount){
setTimeout(function () {
new actualFunction(item,icount);
}, icount);
}
function actualFunction(item,icount){
//...runs ever 1 second
console.log(icount);
}

For Loop running infinitely

So Ive been fighting with this for loop for a day and a half now, when I get it to actually print it goes infinitely, even when the if statement (logCount === 10) is satisfied...
Not too sure what to try anymore, really feel like its far simpler than what I'm trying....
Any attempts at a solution are appreciated..
var timers = [];
var log = (function(outputFunc) {
var counter = 0;
var callerLog = [];
var dateTime = [];
//assigning current Date to a variable to print for logs
let logDate = new Date();
return function(timer) {
for (var logCount = 0; logCount <= 10; logCount++) {
//printing data without using printFunc, as specified
document.getElementById("output").innerHTML += logCount + " " + timer + " " + logDate + "<br>";
//TODO: add after for loop is resolved.
if (logCount >= 10) {
clearInterval(timer1);
clearInterval(timer2);
clearInterval(timer3);
document.getElementById("output").innerHTML += "<br><br/> Logging stopped.";
}
}
}
})(printFunc);
function printFunc(output) {
document.write(output + "<br>");
}
function startMeUp() {
// add each of the timer references to the timers array
// as part of invoking the log function following each interval
timers.push(setInterval("log('Timer1')", 1000));
timers.push(setInterval("log('Timer2')", 1200));
timers.push(setInterval("log('Timer3')", 1700));
}
I'm guessing this is what you're trying to achieve:
function printFunc(output) {
// Replaced with console.log for my own convenience
console.log(output);
}
// Not really a factory, just a curry of the outputFunc
function loggerFactory(outputFunc) {
return function startLogger(name, interval) {
// Variables to keep track of the iterations and a reference to the interval to cancel
let logCount = 0;
let intervalRef;
function tick() {
// On each tick, check if we're done
// If yes, clear the interval and do the last output
// If no, do some output and increment the iterator
// Once the next tick passes we'll check again
// If you were using setTimeout instead you would have to requeue the tick here
if (logCount >= 10) {
clearInterval(intervalRef);
outputFunc('Done ' + name);
} else {
outputFunc(logCount + " " + name);
logCount += 1;
}
}
// Start it of
intervalRef = setInterval(tick, interval);
}
}
const logger = loggerFactory(printFunc);
function startMeUp() {
console.log('Starting');
logger('Log1', 1000);
logger('Log2', 1200);
logger('Log3', 1700);
}
startMeUp();
Some notes:
You could push the intervalRefs into an array but I find it nicer to encapsulate that work within the same logger, since it should only clean up itself anyway.
When working with intervals (or asynchronous code in general) for loops are usually not what you're looking for. For loops are inherently synchronous, all iterations will be run directly after each other, without space for anything else. What you were looking for was a way to run multiple asynchronous "tracks" at the same time. You could use a for loop to start these "tracks", such as:
for () {
logger('Log' + i, 1000 * i);
}
But the key lies in that the logger quickly sets up the interval function and then returns. That way your for loop can quickly "schedule" the tasks but the logger runs the iterations internally asynchronously by using setInterval or setTimeout.

Async foreach in javascript

I hope this will make sense:
I need to create a foreach function in javascript that will be used like this:
foreach(["A", "B", "C"], function(letter, done) {
// do something async with 'letter'
doSomthing(letter, done); // ***
}, function () {
// final callback that is called after all array has been visted.
// do some final work
});
So I was thinking about the following implementation:
var foreach = function(array, func, ready) {
if (!array.length)
ready();
var that = this;
func(array[0], function(){
that.foreach(array.slice(1, array.length), func, ready);
});
}
And it seems that it actually works! very cool.
But I was thinking if there is a solution that doesn't use recursion?
I couldn't think of one...
Your approach is technically correct but it is not good to do in such a way.
Pls implement using promise pattern in javasript .
I recommend you using when.js an open source js available on git for implementing promise pattern Pls refert to the below code
var service = {
fetch: function (query) {
// return a promise from the function
return when(["A", "B", "C"].forEach(function (name) {
alert(name);
}));
}
};
service.fetch("hello world").then(function () {
alert("work has been completed");
});
I just am here again for academic purposes at this point. Now, I recommend everyone to understand Aadit's approach in its elegance and terseness first because it was a nice learning experience for me in his technique of using bind as I did not know about that nor did I know about the additional arguments you can place after setTimeout.
After learning these things, I've reduced my code down to this:
var foreach = function(array,doSomething,onComplete) {
var i = 0, len = array.length, completeCount = 0;
for(;i < len; i++) {
window.setTimeout(function() {
doSomething(arguments[0]);
completeCount++;
if (completeCount === len) {
onComplete();
}
},0,array[i]);
}
};
I argue that you must have a "completeCount" because although Aadit's code is a great terse working solution that is reducing the array automatically, it is not truly asynchronous as "next()" is called after each method completes in the array linearly. The "completeCount" allows code to finish execution in any order which is the point of this I believe. In Aadit's code, there is also the side effect of modifying your input array as well as needing to alter the Function prototype in which I argue this is not necessary. "Hoisting" is also not practiced in his code which I think should be done as that style reduces errors.
Again, I respect Aadit's code very much and have taken the time to come back again to try to present a better solution based on what I have learned through other smart folks as well as Aadit. I welcome any critiques and corrections as I will try to learn from it.
FYI: here is one that is a general deferred method
var deferred = function(methods,onComplete) {
var i = 0, len = methods.length, completeCount = 0,
partialComplete = function() {
completeCount++;
if (completeCount === len) {
onComplete();
}
};
for(;i < len; i++) {
window.setTimeout(function() {
arguments[0](partialComplete);
},0,methods[i]);
}
};
// how to call it
deferred([
function (complete) {
// this could easily be ajax that calls "complete" when ready
complete();
},
function (complete) {
complete();
}
], function() {
alert('done');
});
Check out the async library. It has several different functions like that, and it's actively supported.
Correct me if I am wrong but from what I understand from your question I believe you want to take an array, serially call a function asynchronously on every member of that array, and then execute a callback function when every member of the array has been processed.
Now to execute a function asynchronously in a browser environment we would do something like this:
Function.prototype.async = function () {
setTimeout.bind(window, this, 0).apply(window, arguments);
};
alert.async(5);
alert(6);
In the above example the setTimeout function is used to call the given function asynchronously due to which we first see the value 6 and then the value 5 being alerted.
Next, to make your foreach function asynchronous we would do something as follows:
function forEach(array, funct, callback) {
if (array.length)
funct.async(array[0], forEach.bind(null, array.slice(1), funct, callback));
else callback.async();
}
The above solution doesn't use recusrion. Sure the forEach function is referenced within itself but it's only being called in the funct function which is called asynchronously. Hence the forEach function returns before it's called again in the funct function.
I have included links to JS fiddles before each snippet of code. If you have any more doubts I would be happy to answer them.
Edit:
If you do not like modifying the prototype of Function (#kitgui.com) then you may use this modified code:
var async = Function.prototype.call.bind(function () {
setTimeout.bind(null, this, 0).apply(null, arguments);
});
async(alert, 5);
alert(6);
Since I am not referencing window in the above code it will also work in non-browser environments.
Then we may rewrite the forEach function as follows:
function forEach(array, funct, callback) {
if (array.length)
async(funct, array[0], forEach.bind(null, array.slice(1), funct, callback));
else async(callback);
}
There we have it. No need to modify the prototype of Function. The function body of async is pretty much the same. We simple created an unbound wrapper for it using call.bind. You may see the live demo for yourself.
Bonus:
You can create errbacks using the above pattern as follows (see the live demo):
function forEach(array, funct, callback, error) {
if (array.length && !error)
async(funct, array[0], forEach.bind(null, array.slice(1), funct, callback));
else async(callback, error || null);
}
This is equivalent to the forEachSeries function in caolan's async library in under 10 lines of code.
Assuming you want to do raw computation, and you want it asynchronous so it does not block the browser.
I have been using the "setTimeout(Func,0);" trick for about year. Here is some recent research i wrote up to explain how to speed it up a bit. If you just want the answer, skip to Step 4. Step 1 2 and 3 explain the reasoning and mechanics;
// 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.
///////////////////////////////////////////////
I have found this tutorial that explain exactly what I needed.
I hope this will also help somebody else.
http://nodetuts.com/tutorials/19-asynchronous-iteration-patterns.html#video

Javascript async loop processing

I have a javascript loop that takes some time to process. I wish I could slim it down but it has to process a large amount of data. While it's running the browser becomes unresponsive of course. I've read the best way to handle this in javascript is using an asynchronous loop of some sort. This way mouse clicks, etc, can continue to be processed in between loop processing. Is there any standard async frameworks that will work well for this? Or can someone provide a simple example of how this might be coded? Thanks!
Sadly WebWorkers are not available yet on everyone's browser. I have been using the "setTimeout(Func,0);" trick for about year. Here is some recent research i wrote up to explain how to speed it up a bit. If you just want the answer, skip to Step 4. Step 1 2 and 3 explain the reasoning and mechanics;
// 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.
///////////////////////////////////////////////
Simply break the work up in to chunks and process one chunk at a time. The code here is a good starting place, but use setImmediate or setTimeout to call the next iteration of the loop.
The proper way to solve your problem is to use Web Workers, which execute code on a separate thread.
You can simply wrap each iteration of the loop in a setTimeout like so (see jsfiddle):
$(document).ready(function(){
var COUNT = 100000;
function process(item){
var r = 0;
for(var i=0; i < item; i++){
r += i;
}
return r;
}
for(var i=0; i < COUNT; i++){
(function(item){
setTimeout(function(){
$('#log').html("Processing #" + item + " (" + process(item) + ")");
});
})(i);
}
});

Categories

Resources