Delay JavaScript's function execution - javascript

I have a JQuery's .each loop that calls a function with a parameter per iteration, is there a way to delay this function call? I have tried setTimeout as in the following but this does not work as the function gets executed immediately.
$.each(myArray, function (j, dataitem)
{
setTimeout(function () { showDetails(dataitem) }, 300);
});
function showDetails(dataitem)
{
...
}
Array size is roughly 20, What I'm trying to do is to distribute function calls over a certain time frame instead of immediately, any idea how to achieve this? I'm prepared to rewrite and restructure how functions get called to get this done, any help would be appreciated.

You could use the index of the array to calculate the interval dynamically:
$.each(myArray, function (j, dataitem) {
window.setTimeout(function () {
showDetails(dataitem)
}, (j + 1) * 300);
});

You execute them all after 300 milliseconds. Instead, try something like this:
window.setTimeout(function () { showDetails(dataitem) }, (j + 1) * 300);
Edit: instead of creating 20 timers at once I think it's better to do it one by one. Function should be:
function showDetails(index)
{
if (index >= myArray.length)
return false;
var dataItem = myArray[index];
//code here......
//code here......
//code here......
windows.setTimeout(function() { showDetails(index + 1); }, 300);
}
And first call can be:
$(document).ready(function() {
{
showDetails(0);
});
This assume myArray is plain global array, and will handle one item and only then call the next item with delay.

Take a look at jQuery.queue([ queueName ], callback( next )). This allows you to queue functions up to be called and is what jQuery's animation effects use internally.
It sounds like you would like to implement a queue, although it is not entirely clear you intentions for doing so.
EDIT: re-reading your question, I think other answers better match what you are after, however I thought that I would show you an example of how to achieve delayed function execution with a custom queue.
An example of how you could use a queue.
var myArray = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20],
output = $('#output');
// set the queue up
$.each(myArray, function (j, dataitem) {
output.queue('queue', function(next) {
var that = this;
showDetails(dataitem);
window.setTimeout(next,300);
});
});
// start the queue running.
output.dequeue('queue');
function showDetails(dataitem) {
output.append('<div>' + dataitem + '</div>');
}

Just don't use $.each, but something like:
var data = [1, 2, 3, 4, 5];
function showDetails(values, delay) {
console.log(values.shift()); //show the value
if (values.length) {
setTimeout(function() {showDetails(values, delay); }, delay); //schedule next elem
}
}
showDetails(data.slice(0), 300); //dont forget the slice, call-by-reference

Related

Calling two javascript functions one from each other

I have two functions for eg., runslider() and runslider1().
runslider() runs after the document is loaded and I need to call runslider1() after finishing runslider(). Then again runslider() after runslider1(). This process should happen like infinite loop. Can someone help me please.
I have tried to keep them like callbacks. But that didn't work.
function runSlider(runslider1){
alert("run")
runSlider1(runSlider());
}
function runSlider1(runslider){
alert("run1");
runSlider(runSlider1());
}
if you want your functions to be called over and over again try using setInterval
function runSlider(){
alert("run");
runSlider1();
}
function runSlider1(){
alert("run1");
}
setInterval(runSlider, 100);
This will cause both functions to be called in that order repeatedly every 100ms. It seems like this is the behavior you are looking for.
The comments above are correct - you will cause a stack overflow.
Don't know why you would need this, but I cleaned your code for you:
function runSlider() {
alert('run');
runSlider1();
}
function runSlider1() {
alert('run1');
runSlider();
}
You can create infinite loop like this you just need to call one function.
var runSlider = function() {
console.log("run")
runSlider1()
}
var runSlider1 = function() {
console.log("run1");
setTimeout(function() {
runSlider()
}, 1000)
}
runSlider()
Another solution is:
function runSlider() {
console.log("run");
runSlider1();
setTimeout(runSlider1(), 1000) // Calls runSlider1() after 1 second(1000 millisecond). You can change it.
}
function runSlider1() {
console.log("run1");
setTimeout(runSlider(), 1000) // Calls runSlider1() after 1 second(1000 millisecond).
}
runSlider(); // Starts the cycle
var maxCalls = 0;
function run1(cb) {
alert('run1');
if (maxCalls++ < 5) { //Added this to avoid an infinite loop
cb(run1); //We want the function run after cb to be this one
}
}
function run2(cb) {
alert('run2');
if (maxCalls++ < 5) {
cb(run2);
}
}
This is the way to call one function from another. If you create an infinite loop, you will freeze the browser up. If you want the two functions running constantly, its best to release execution for a bit with a setInterval call instead.
var runFunc = 0;
var run1 = function() {
alert('run1');
};
var run2 = function() {
alert('run2');
};
var run = function() {
!(++runFunc) ? run2 : run1; //Alternate between calling each function
}
var stopRunning = function() { //Call this to stop the functions running
clearInterval(runInterval);
};
var runInterval = setInterval(run, 1000); //Calls the function every second

simultaneous functions javascript

I got this code, it's supposed to toggle elements, following a repetitive patron, that will grow up randomly, my start function executes my runp() function at simultaneous, and it got all messy. i would need to wait until runp() finishes to continue executing. Thanks
function runp(patron){
var x = 0;
var intervalID = setInterval(function () {
$("#container"+patron[x]).toggle(1).delay(1000).toggle(1).delay(1000);
if (++x === 20) {
window.clearInterval(intervalID);
}
}, 2000);
}
function start(patron, patronl){
while (patron.length<20){
patron.push(rand(1,4));
runp(patron);
}
}
You can use .queue()
// alternatively pass randomly shuffled array of elements to `$.map()`
$({}).queue("toggle", $.map($("[id^=container]"), function(el) {
return function(next) {
return $(el).toggle(1).delay(1000).toggle(1).delay(1000)
.promise().then(next)
}
})).dequeue("toggle")

Complete all functions inside a FOR LOOP before iterating to next iteration of the loop

Say i have a function like :
var bigArray = [1,2,3,4,5.........n];
for(var i=0; i<bigArray.length; i++){
if(someCondition){
setTimeout(function(){
..do_stuff..
callSomeFunction();
}, someDelayTime);
}
if(someCondition){
setTimeout(function(){
..do_stuff..
callSomeFunction();
}, someDelayTime);
}
if(someCondition){
setTimeout(function(){
..do_stuff..
callSomeFunction();
}, someDelayTime);
}
.
.
.
.
.
.
.
.
if(someCondition){
setTimeout(function(){
..do_stuff..
callSomeFunction();
}, someDelayTime);
}
}
Now what i want from this function is that when all of the conditions have performed their intended task, only then it should move to the next iteration of FOR loop. In other words, i=0 in FOR LOOP should change to i=1 and so-on iff all of the conditions inside the FOR loop have completed their job in current iteration.
Currently behaviour of this code is very random (because of setTimeout i believe). How can i make this work as per my expectations?
I recently read about promises (don't know much about them) but i'm not sure how to implement them in this code or if they will work in this case...
The original idea came from the answer of Nina Scholz on some similar question.
this answer is good if you don't like promises and deferred object. Othewise Kris Kowal's Q library would be a better choice.
function Iterator() {
var iterator = this;
this.queue = [];
this.add = function(callback, wait) {
iterator.queue.push(iterator.wait(wait));
iterator.queue.push(function() {
callback();
iterator.next();
});
};
this.wait = function(wait) {
return function() {
setTimeout(iterator.next, wait);
};
};
this.next = function() {
iterator.queue.length && iterator.queue.shift()();
};
}
var arr = [1, 2, 3, 4, 5],
counter = -1;
var iterator = new Iterator();
(function fillNextIteration() {
if (counter >= arr.length)
return;
counter++;
iterator.add(function() {
console.log('1 - counter value is ' + counter);
}, 100);
iterator.add(function() {
console.log('2 - counter value is ' + counter);
}, 100);
iterator.add(function() {
console.log('3 - counter value is ' + counter);
}, 100);
iterator.add(function() {
console.log('4 - counter value is ' + counter);
}, 100);
iterator.add(fillNextIteration, 100);
iterator.next();
})();
Code Explanation
Iterator class has one queue which is and array and some methods:
next
when you call next() if there is any callback in queue it will fetch first and execute that. when you call Array#shift it removes and returns first item from array. When this item is a function then you can invoke it by adding parentheses in front of it. here is the expanded version:
if (iterator.queue.length > 0) {
var callback = iterator.queue.shift();
callback();
}
wait
This method will add an extra callback to queue which after a timeout calls the next method. this one is for creating the expected delay.
add
This method calls the wait with desired delay then attaches another function to queue which will call the callback and then calls next to make callback chain running.
fillNextIteration
after explaining the iterator there is another loop here on function fillNextIteration this starts with the conditions for the first iteration for example:
if (someConditionIsTrue) {
iterator.add(function() {
doWhatShallBeDone;
}, delay);
}
you can check all conditions and then if required add the callback as shown.
after all condition finished add fillNextIteration as last action to continue the loop.
self invoking function
As you can see fillNextIteration is self invoked.
This helps to reduce one invoke and works like this:
function f(){};
f();
Last thing to mention
you can create a loop by calling a function inside itself. if there is no delay or stop for revoke then it would be a deadlock. So this is another loop as well:
(function loop() { setTimeout(loop, delay); })();
Try this please :
bigArray = [1, 2, 3, 4, 5.........n];
neededStuff = 10; // Number of finished works you need below
stuffOK = neededStuff;
function mainFunction(i) {
function loopFunction(i) {
if (someCondition) {
setTimeout(function () {
// .. do_stuff..
stuffOK++;
callSomeFunction();
}, someDelayTime);
}
if (someCondition) {
setTimeout(function () {
// .. do_stuff..
stuffOK++;
callSomeFunction();
}, someDelayTime);
}
if (someCondition) {
setTimeout(function () {
// .. do_stuff..
stuffOK++;
callSomeFunction();
}, someDelayTime);
}
/*
...
*/
if(someCondition) {
setTimeout(function () {
// .. do_stuff..
stuffOK++;
callSomeFunction();
}, someDelayTime);
}
}
if (stuffOK == neededStuff) {
i++;
stuffOK = 0; // reinit counter
loopFunction(i);
}
setTimeout('mainFunction(' + i + ');', 100);
}
mainFunction(-1);
Hope this will work from promise chaning
callSomeFunction1().then(function() {
callSomeFunction2();
}).then(function() {
callSomeFunction3();
})

Calling a function recursively with setTimeout

I want call few function one after another recursively with setTimeout.
var flag = 0 ;
function slave1(){
if(flag < 60) {
var COPY_PO_LINE_DIV = document.getElementById("DOM_ELEMENT1"); // Checking if DOM has loaded or not. If yes then doing something.
if (COPY_PO_LINE_DIV != null) {
flag = 0;
//doing something
} else {
setTimeout(slave1,2000); //waiting for 2 seconds and checking again.
}
}
}
//doing similar task
function slave2(){
if(flag < 60) {
var COPY_PO_LINE_DIV = document.getElementById("DOM_ELEMENT2");
if (COPY_PO_LINE_DIV != null) {
flag = 0;
//doing something
} else {
setTimeout(slave2,2000);
}
}
}
function master() {
slave1();
console.log("Without completing slave1 function.");
slave2();
}
Through master() function I want to call multiple functions one after another, however in current situation its calling slave2() without completing slave1(). How can I make sure that slave1() has executed completed. If DOM element is not loaded than it should execute 60 times after every 2 seconds and than it should come out from slave1() and go to next one.
I want to execute same function for 60 times if dom element is not loaded without returning the control to next function.
You need to adjust slave1 to run a callback when it is finished which will be slave2.
function slave1(callback){
if(flag < 60) {
var COPY_PO_LINE_DIV = document.getElementById("DOM_ELEMENT1"); // Checking if DOM has loaded or not. If yes then doing something.
if (COPY_PO_LINE_DIV != null) {
flag = 0;
//doing something
callback();
} else {
setTimeout(slave1,2000); //waiting for 2 seconds and checking again.
}
}
}
function slave2(){...}
function master() {
slave1(slave2);
console.log("Without completing slave1 function.");
}
This is your basic javascript chaining. If you have more slaves you might want to look into async.series otherwise you go into callback hell as Gabs00 has put it nicely:
slave1(function(){
slave2(function(){
slave3(function(){
slave4(slave5);
});
});
});
If you need to pass values to callbacks then you need to use an intermediate anonymous function which in turn calls the intended callback with the arguments in question. To do that, you need define your functions so that they use the arguments:
function slave1(str, callback){...}
function slave3(i, callback){...}
slave1("some argument", function(){
slave2("another argument", function(){
slave3(1, function(){
slave4(2, slave5);
});
});
});
Consider using promises for things like that. Here an implementation on top of jQuery, other promise libraries work similarly.
function waitForElement(elementId, maxTries, checkInterval) {
var d = $.Deferred(), intvalID, checkFunc;
// set up default values
maxTries = maxTries || 60;
checkInterval = checkInterval || 2000;
checkFunc = function () {
var elem = document.getElementById(elementId);
if (maxTries-- > 0 && elem) {
clearInterval(intvalID);
d.resolve(elem);
}
if (maxTries <= 0) {
clearInterval(intvalID);
d.reject(elementId);
}
};
// set up periodic check & do first check right-away
intvalID = setInterval(checkFunc, checkInterval);
checkFunc();
return d.promise();
}
Now, if you want to test for elements one after another, you can cascade the calls like this:
function master() {
waitForElement("DOM_ELEMENT1").done(function (elem1) {
waitForElement("DOM_ELEMENT2").done(function (elem2) {
alert("elem1 and elem2 exist!");
// now do something with elem1 and elem2
}).fail(function () {
alert("elem1 exists, but elem2 was not found.");
});
}).fail(function () {
alert("elem1 not found.");
});
}
or you can do it in parallel and have a callback called when all of the elements exist:
function master() {
$.when(
waitForElement("DOM_ELEMENT1"),
waitForElement("DOM_ELEMENT2")
)
.done(function (elem1, elem2) {
alert("elem1 and elem2 exist!");
// now do something with elem1 and elem2
})
.fail(function () {
alert("not all elements were found before the timeout");
});
}
Your slave2 function should be passed to slave1 function as a callback and should be called in slave1 after it finishes (if ever?). Your current situation is quite common, since setTimeout() function is asynchronous, thus JS interpreter doesn't wait till the function is completed, but sets the setTimeout() result at the end of the Evet Loop and continues processing the master() method.
In order to pass arguments to functions, creating anonymous functions turns out to be an overkill. Consider using "bind" instead. So, if you've got
function slave1(str, callback){...}
function slave2(str, callback){...}
function slave3(i, callback){...}
function slave4(i, callback){...}
function slave5()
Instead of using
slave1("some argument", function(){
slave2("another argument", function(){
slave3(1, function(){
slave4(2, slave5);
});
});
});
Consider using
slave1("some argument",
slave2.bind(null, "another argument",
slave3.bind(null, 1,
slave4.bind(null, 2, slave5)
)
)
);
Much easier, more efficient in terms of memory and CPU utilization.
Now, how to do this with setTimeout:
slave1("some argument",
setTimeout.bind(null, slave2.bind(null, "another argument",
setTimeout.bind(null, slave3.bind(null, 1,
setTimeout.bind(null, slave4.bind(null, 2,
setTimeout.bind(null, slave5, 0)
),0)
),0)
),0)
);
I explained the problem in more detail at
http://morethanslightly.com/index.php/2014/09/executables-the-standard-solution-aka-mind-the-bind/

How do I do a yielding loop with underscore.js?

So, I want to loop through several hundred items and not block the UI thread while I process each -- altogether possibly a few seconds of work, so I'd like to yield every so often. Several books recommend a loop that looks like this:
function processArray(items, process, callback){
var todo = items.concat(); //create a clone of the original
setTimeout(function () {
process(todo.shift());
if (todo.length > 0) {
setTimeout(arguments.callee, 100);
} else {
callback(items);
}
}, 100);
}
(ref http://answers.oreilly.com/topic/1506-yielding-with-javascript-timers/ )
Last time I used a clever loop, I found out that underscore already supported it and probably had better, more stable, etc. version. How do I do the above in underscore? _.each doesn't seem to apply, _.each doesn't appear to yield or provide options for changing the pause time.
Look into the Async library
https://github.com/caolan/async
And make process an asynchronous function that accepts a callback.
function process(item, cb){
//replace this code block with your actual process logic
setTimeout(function () {console.log(item); async.nextTick(cb);}, 500);
}
function processArray(items, iterator, callback){
var todo = items.concat(); //create a clone of the original
async.eachLimit(todo, 4, iterator, function(){
//replace 4 with the desired number of simultaneous asynchronous operations
//if `process` isn't too computationally expensive, you could try 50
callback(); //all done
});
}
processArray([0,1,2,3,4,5,6,7,8,9,10], process, function(){
console.log('all done');
});
demo: http://jsbin.com/izumob/1/
I suggest you use an interval instead. That way, you are not calling setTimeout repeatedly, as well as building an anonymous function on every iteration:
function processArray(items, process, callback) {
var todo = items.concat();
//our interval "loop"
var loop = setInterval(function () {
process(todo.shift());
//"break" the interval when nothing left
if(!todo.length) {
clearInterval(loop);
callback(items);
}
}, 100);
}

Categories

Resources