Finish running a function before proceeding in a loop - javascript

I want the function in my FOR loop to finish running before going on to the next code but I just can't get it to work.
It should fade in fade out the text "Result" before popping the alert window 'Lucky you' but I would see the alert window loop first before seeing the text. I don't know what I'm doing wrong.
HTML:
<div id="quotes" style="display: none;">Result</div>
Javascript:
function showNextQuote() {
$("#quotes").fadeIn(2000).delay(2000).fadeOut(2000);
}
for (i = 1; i < 4; i++){
showNextQuote(); alert("Lucky you");
};

Since these operations are asynchronous you can't block processing while they're running. Instead, you pass them a function to execute when they complete (called a callback). So chaining them like this would look more like a recursive function than a loop.
(Though this isn't really technically recursion since the asynchronous operations aren't building up a stack. It's semantically/logically very similar to tail recursion though, at least in function structure.)
Something like this:
function showNextQuote(iteration) {
// terminating condition
if (iteration > 4) {
return;
}
// recurse
$("#quotes").fadeIn(2000).delay(2000).fadeOut(2000, function () {
alert("Lucky you");
showNextQuote(iteration + 1);
});
}
showNextQuote(1);
What this does is define the function, which internally passes a reference to itself as a callback to fadeOut. Each callback increments the iteration value by 1. Then after defining the function it's invoked with an initial iteration value of 1.

As said in comments, JavaScript can't do this in a loop, since fadeIn, delay and fadeOut are all asynchronous.
Something like this should work:
function showNextQuote(n) {
if (n > 0) {
$("#quotes").fadeIn(2000).delay(2000).fadeOut(2000, function() {
alert("Lucky you");
showNextQuote(n - 1);
});
}
}
showNextQuote(4);

Related

Only Execute setInterval function in javascript

var intervalHandle ;
var currentOpacity=0;
function beginAnimate() {
current_question_div = document.getElementById(current_question);
intervalHandle = setInterval(function(){
animateBox(current_question_div)
}, 200);
alert("Hellow ashik !");
}
function animateBox(current_question_div) {
current_question_div.style.backgroundColor = "lightblue";
currentOpacity = currentOpacity + .091;
current_question_div.style.opacity = currentOpacity;
if(currentOpacity > 1) {
alert("END");
clearInterval(intervalHandle);
}
}
<P onclick="beginAnimate">Click</p>
Everything is ok, but alert("Hellow ashik !"); working while interval is in execution. I want to open the alert("Hellow ashik !") When the clearInterval occurred. Means now other JavaScript code is executing in parallel while interval is also executing. I need to execute only one thread at a time. Please help. Is there a way to execute this code one by one. Thanks.
This code should accomplish what you want:
var intervalHandle,
currentOpacity = 0;
function beginAnimate()
{
current_question_div=document.getElementById(current_question);
/* call any other functions that need to run before the animation starts here */
alert("Hellow ashik !");
intervalHandle = setInterval(function(){
// note 'endAnimate()' is passed to the 'animateBox()' function as a callback
animateBox(current_question_div, endAnimate)
}, 200);
/* call any functions that can run while the animation is running here */
}
function endAnimate() {
alert("The end!");
/* call any other functions that need to be run after the animation here */
}
function animateBox(current_question_div, callback) {
current_question_div.style.backgroundColor = "lightblue";
currentOpacity = currentOpacity + .091;
current_question_div.style.opacity = currentOpacity;
if(currentOpacity>1)
{
alert("END");
clearInterval(intervalHandle);
if (callback && typeof(callback) === 'function') {
callback();
}
}
}
<P onclick="beginAnimate">Click</p>
JavaScript is a single threaded language -- ignoring WebWorkers or other workarounds. However, it is asynchronous and because of that you often see behavior that looks like threading. One way to work around these issues is to use the callback pattern.
In the callback pattern, you pass a function to be called later when your asynchronous function completes. JavaScript makes this simple as functions are first class objects and can be passed or assigned to just as easily as a number.
In a browser, javaScript has a single-threaded nature, thus it executes on a single thread per window. This means that the execution diagram is one dimensional: javaScript can only ever execute one piece of code at a time.
The browsers are event-driven. Most events are asynchronous, such as mouse clicks, key press, and timers. They are only run when there is an opening in the execution; and in the meantime, they are forced to get into the event queue waiting for execution.
<p id="test"></p>
document.getElementById("test").innerHTML += "a";
var intervalHandle = setInterval(function(){
callback();
}, 200);
document.getElementById("test").innerHTML += "c";
function callback() {
document.getElementById("test").innerHTML += "b";
//here where should be located all the code supposed to be executed after the timeinterval.
clearInterval(intervalHandle);
//here where should be located all the code supposed to be executed after the clearInterval.
}
Check this link jsfiddle to see the working example.
In the example above, the javascript thread starts setting the content of p to "a", then delays the execution of callback. This goes to the Event queue on the nearest timer tick after 200ms. And then continue execution, setting the content of p to "c".
So if you want to execute something after the clearInterval, then you should place it after the clearInterval.
Hope it's useful!

setTimeout and recursive function with parameters

I'm trying to do something like that :
I want to call the doSomething() function only when the tab has loaded class (it may take 3 or 5 seconds).
function addInfosSinistre(msg){
addInfosSinistreRecursive(msg, 0);
}
function addInfosSinistreRecursive(msg, nbCalls) {
if ($("#tab").hasClass('loaded')) {
doSomething();
}else if (nbCalls < 10) {
setTimeout(addInfosSinistreRecursive(msg, nbCalls+1),500);
}
}
This is not working, it seems like the timer doesn't work. Is there something wrong here ?
What you actually do, is to execute the function addInfosSinistreRecursive and the pass the return value to setTimeout(). What you actually want to do is to pass a function reference.
One way of doing so is to create a closure for the variables like this:
function addInfosSinistreRecursive(msg, nbCalls) {
function timeoutHandler(){
if ($("#tab").hasClass('loaded') ) {
doSomething();
}else if (nbCalls < 10) {
nbCalls += 1;
setTimeout( timeoutHandler, 500 );
}
}
// first execution
timeoutHandler();
}
The function timeoutHandler() can reference the parameters to addInfosSinistreRecursive() and has no parameters of its own. Hence you can pass a reference to it to setTimeout().
Yes, there's definitely something wrong here. You're immediately calling the function and passing undefined to setTimeout() instead of giving it a function to call. This would do what you are trying to do:
function addInfosSinistreRecursive(msg, nbCalls) {
if ($("#tab").hasClass('loaded')) {
doSomething();
}else if (nbCalls < 10) {
setTimeout(addInfosSinistreRecursive.bind(this, msg, nbCalls + 1), 500);
}
}
But the question is why are you trying to do this? It seems like a convoluted way to do whatever it is you are trying to accomplish. You should probably be using an event somewhere when the tab is changed rather than recursively looping and waiting for the tab to change.

Prevent JS code to run, before function has ended

I can't wrap my head around this one?
WHY IS IT THAT MY:
.each loop runs right through even though I'm stalling things inside by 1000ms per loop?
The problem is that the window.location.href command runs TO EARLY before the setTimeout has finished? Same for the stopload() function which is also ended to early? I have seen something about recursive setTimeout functions is that what is needed here and how do I implement that?
function shop(clickedButton)
{
var buttonvalue = $(clickedButton).val();
startLoad();
pdel = 1000;
$("input:submit[value='buy']").each(function(index)
{
if(index != 1)
{
$("#backgroundPopup").text(index);
var submithing = this;
setTimeout(function(){ clicksubmitbutton(submithing); },pdel);
pdel += 1000;
}
});
stopLoad();
if(buttonvalue == "1")
{
window.scrollTo(0,0);
}
else
{
window.location.href = 'http://my.url';
}
}
Javascript has no notion of a sleep, or stall. Execution continues right past a setTimeout call. This function simply schedules a function to be run after the given number of milliseconds.
In order to get your desired behavior, you need to call the next iteration in the setTimeout callback function.
Something like:
function submitTheThing(index) {
if (done) { //some logic
return;
}
// do something
setTimeout(function() { submitTheThing(index+1); }, 1000);
}

jQuery Asynchronous each() function - how to exit the callback?

I'm using the Asynchronous each() function plugin code posted at:
jQuery Tips and Tricks
It works well but I can't seem to exit a running callback function using return; or return true;. the consquence is that when the condition to "exit" is met, it stops all remaining $.forEach "loop" execution.
Since the plugin implements a setTimeout loop approach, perhaps I just need to know how to exit an already running setTimeout callback function? BTW - I'm avoiding using for loops or $.each() to avoid browser lockup while big json processing occurs. "backgrounding" the loop significantly improves performance of the UI.
$.forEach(json, 1000, function(idx,item) {
if(some_condition) return true; //exit this timeout iteration
//otherwise do something
});
jQuery.forEach = function (in_array, in_pause_ms, in_callback)
{
if (!in_array.length) return; // make sure array was sent
var i = 0; // starting index
bgEach(); // call the function
function bgEach()
{
if (in_callback.call(in_array[i], i, in_array[i]) !== false)
{
i++; // move to next item
if (i < in_array.length) setTimeout(bgEach, in_pause_ms);
}
}
return in_array; // returns array
};
jQuery.fn.forEach = function (in_callback, in_optional_pause_ms)
{
if (!in_optional_pause_ms) in_optional_pause_ms = 10; // default
return jQuery.forEach(this, in_optional_pause_ms, in_callback); // run it
};
Thanks much!
From the docs:
We can break the $.each() loop at a particular iteration by making the
callback function return false.
EDIT: This also applies to your custom forEach.

determining the end of asynchronous operations javascript

If I have a function that's passed this function:
function(work) {
work(10);
work(20);
work(30);
}
(There can be any number of work calls with any number in them.)
work performance some asynchronous activity—say, for this example, it just is a timeout. I have full control over what work does on the completion of this operation (and, in fact, its definition in general).
What's the best way of determining when all the calls to work are done?
My current method increments a counter when work is called and decrements it when it completes, and fires the all work done event when the counter is 0 (this is checked after every decrement). However, I worry that this could be a race condition of some sort. If that is not the case, do show my why and that would be a great answer.
There are a ton of ways you can write this program, but your simple technique of using a counter will work just fine.
The important thing to remember, the reason this will work, is because Javascript executes in a single thread. This is true of all browsers and node.js AFAIK.
Based on the thoughtful comments below, the solution works because the JS event loop will execute the functions in an order like:
function(work)
work(10)
counter++
Start async function
work(20)
counter++
Start async function
work(30)
counter++
Start async function
-- back out to event loop --
Async function completes
counter--
-- back out to event loop --
Async function completes
counter--
-- back out to event loop --
Async function completes
counter--
Counter is 0, so you fire your work done message
-- back out to event loop --
There's no race condition. There is the added requirement for every request made to perform a decrement when it's finished (always! including on http failure, which is easy to forget). But that can be handled in a more encapsulated way by wrapping you calls.
Untested, but this is the gist (I've implemented an object instead of a counter, so theoretically you can extend this to have more granular queries about specific requests):
var ajaxWrapper = (function() {
var id = 0, calls = {};
return {
makeRequest: function() {
$.post.apply($, arguments); // for example
calls[id] = true;
return id++;
},
finishRequest: function(id) {
delete calls[id];
},
isAllDone: function(){
var prop;
for(prop in calls) {
if(calls.hasOwnProperty(prop)) {return false;}
}
return true;
}
};
})();
Usage:
Instead of $.post("url", ... function(){ /*success*/ } ... ); We'll do
var requestId;
requestId = ajaxWrapper.makeRequest("url", ...
function(){ /*success*/ ajaxWrapper.finishRequest(requestId); } ... );
If you wanted to be even more sophisticated you could add the calls to finishRequest yourself inside the wrapper, so usage would be almost entirely transparent:
ajaxWrapper.makeRequest("url", ... function(){ /*success*/ } ... );
I have an after utility function.
var after = function _after(count, f) {
var c = 0, results = [];
return function _callback() {
switch (arguments.length) {
case 0: results.push(null); break;
case 1: results.push(arguments[0]); break;
default: results.push(Array.prototype.slice.call(arguments)); break;
}
if (++c === count) {
f.apply(this, results);
}
};
};
The following code below would just work. Because javascript is single threaded.
function doWork(work) {
work(10);
work(20);
work(30);
}
WorkHandler(doWork);
function WorkHandler(cb) {
var counter = 0,
finish;
cb(function _work(item) {
counter++;
// somethingAsync calls `finish` when it's finished
somethingAsync(item, function _cb() {
finish()
});
});
finish = after(counter, function() {
console.log('work finished');
});
};
I guess I should explain.
We pass the function that does work to the workhandler.
The work handler calls it and passes in work.
The function that does work calls work multiple times incrementing the counter
Since the function that does work is not asynchronous (very important) we can define the finish function after it has finished.
The asynchronouswork that is being done cannot finish (and call the undefined finish function) before the current synchronous block of work (the execution of the entire workhandler) has finished.
This means that after the entire workhandler has finished (and the variable finish is set) the asynchronous work jobs will start to end and call finish. Only once all of them have called finish will the callback send to after fire.

Categories