sequential functioncalls - javascript

I am communicating with a servlet that changes and saves an external file. Since this takes some time, I need some of my javascript functioncalls to happen sequentially so that the actions of one function don't interfear the actions of another function.
To do this, I wrote a 'sequential' function that takes another function that can only be called when the busyflag is set to false (i.e. when no other functioncall is handled at the same time). This is my code:
var busy = false;
function sequential(action) {
while(busy)
setTimeout(function(){sequential(action);}, 10);
busy = true;
action();
setTimeout(function(){busy = false;}, 100);
}
function test1() {sequential(function() {alert("test1");});}
function test2() {sequential(function() {alert("test2");});}
And this is the example on jsFiddle. For some reason this code this code keeps looping on the second call (when a functioncall has to wait).

while(busy)
setTimeout(function(){sequential(action);}, 10);
setTimeout does not block, it returns immediately and allows the loop to continue. Javascript is single threaded, so this loop just keeps running and prevents any other code from executing, meaning busy will never get set to false to exit the loop.
Assuming these things you are waiting on are ajax calls, you will likely want to use some sort of queue and then in the callback of the ajax call, run the next request.

I presume your javascript is making ajax calls to your server.
If you need the different calls to run one after the other, then you should get your javascript code to set up hooks to wait until it gets results back from one call before making the next request.
I recommend using a javascript toolkit like jQuery for these purposes. It makes problems like this much easier to solve. Every ajax method in jQuery accepts at least a callback that will be called when the query is complete. For jQuery.ajax() you can go
$.ajax(...).done(function() {
// This part will be run when the request is complete
});
And for .load():
$("#my_element").load(url,data,function() {
// This part will be run when the request is complete
});

I first implemented a solution like suggested by James Montagne, but after some searchin I found out that you can use the onreadystatechange property of the XMLHttprequest to set the busy-flag to true.
This code works like expected:
function sequential(action) {
if (busy) setTimeout(function(){sequential(action);}, 20);
else action();
}
function send(message){
busy = true;
var request = new XMLHttpRequest();
request.open("POST", "owlapi", true);
request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
request.send(message);
request.onreadystatechange = function() {busy = false;};
}

Related

Ajax call not happening immediately

I am watching this Youtube video where he explains Call Stack, Event loop in case of Async events like Ajax calls. He said JavaScript execution happens line by line. Let's execute a sample program:
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
console.log(xhttp.responseText);
}
};
xhttp.open("GET", 'https://httpbin.org/get', true);
xhttp.send();
let i = 1;
while (i < 1000000000) {
i++;
}
console.log(i);
So, my understanding is that the JS Engine will place Ajax call in the Call Stack and make Ajax Call. Since it takes some time, it will pass the callback to browser Web APIs and continue with rest of the code (while loop in our case). When Ajax call is done, Web Apis will put the callback in Task Queue. If the Call Stack is empty, Event Loop will place the Callback in Stack again and the callback gets executed. In our program, I intentionally made count large to make Event Loop wait for while loop to complete even though Ajax call is done. But When I ran the above program, the ajax call was made only after the loop is completed. Then what's the point in writing the ajax call before while loop. I am expecting Ajax call to fire immediately but print response after while loop is done. Did I misunderstand something?
I'll take a stab at answering this, if anyone else can explain it in more detail or can correct me, it'll be a good learning experience as well.
AJAX = Asynchronous Javascript And XML.
The name itself it stating that the function will be asynchronous.
According to the video that OP has linked, it seems that the explanation is very clear on what async codes is. Let's focus on the 'blocking' part of Javascript.
In your example, that AJAX call is added to the stack, but because it is an async function, it will not stop the next piece of code from running, your while loop. The while loop is synchronous, so it WILL stop everything and let the while runs (which means your async is now blocked as well).
Since console.log() is a faster function than your AJAX result, it will print first and then followed by the result of the http call.
src: https://www.youtube.com/watch?v=8aGhZQkoFbQ&t=412s

javascript outside of scope

Based on chrome developer tools a breakpoints I think I'm dealing with a scope issue I can figure out. Is it the way I define the function? The script below is an include js file and the array ' timeStamp I want available for use in other functions without having to call my loadData function everytime.
The timeStamp array goes undefined once it leaves the for loop before it even leaves the function.
var timeStamp = []; // Want this array to be global
function loadData (url){
$.getJSON(url, function(json) {
for (var i=0;i<json.length;i++){
timeStamp.push(json[i].TimeStamp);
}
console.log(inputBITS); //returns the value
});
console.log(inputBITS); //undefined
}
Thank you for anyhelp
It looks like the issue is that getJSON is asynchronous. When it executes and finishes and your code continues on, it indicates only the START of the networking operation to retrieve the data. The actual networking operation does not complete until some time later.
When it does complete, the success handler is called (as specified as the second argument to your getJSON() call) and you populate the timeStamp array. ONLY after that success handler has been called is the timeStamp array valid.
As such, you cannot use the timeStamp array in code that immediately follows the getJSON() call (it hasn't been filled in yet). If other code needs the timeStamp array, you should call that code from the success handler or use some other timing mechanism to make sure that the code that uses the timeStamp array doesn't try to use it until AFTER the success handler has been called and the timeStamp array has been populated.
It is possible to make some Ajax calls be synchronous instead of asynchronous, but that is generally a very bad idea because it locks up the browser during the entire networking operation which is very unfriendly to the viewer. It is much better to fix the coding logic to work with asynchronous networking.
A typical design pattern for an ajax call like this is as follows:
function loadData (url){
$.getJSON(url, function(json) {
// this will execute AFTER the ajax networking finishes
var timeStamp = [];
for (var i=0;i<json.length;i++) {
timeStamp.push(json[i].TimeStamp);
}
console.log(timeStamp);
// now call other functions that need timeStamp data
myOtherFunc(timeStamp);
});
// this will execute when the ajax networking has just been started
//
// timeStamp data is NOT valid here because
// the ajax call has not yet completed
// You can only use the ajax data inside the success handler function
// or in any functions that you call from there
}
And here's another person who doesn't understand basic AJAX...
getJSON is asynchronous. Meaning, code keeps running after the function call and before the successful return of the JSON request.
You can "fix" this by forcing the request to be synchronous with an appropriate flag, but that's a really bad idea for many reasons (the least of which is that you're violating the basic idea of AJAX). The best way is to remember how AJAX works and instead put all your code that should be executed when the AJAX returns, in the right place.

Detecting When Javascript is Done Executing

Is there an event in javascript that I could bind some sort of listener to that will tell me when all javascript/jQuery/Ajax is done executing on the page? The page will not be loading/unloading/reloading, etc between the time the execution begins and the time that I need the listener to "listen", so those events don't work. The page literally is not doing anything. The button is clicked and some javascript functions fire which contain Ajax calls to web services. After all have finished, I want to change window.location. But window.location is changing before the web services have finished in my case.
Currently using setTimeout to achieve this, but as sometimes the code needs more time to run than normal, sometimes the window.location is firing before all the other javascript has finished. Simply put
<input type = "button"... onclick="doThis();";
function doThis() {
try{
//Contains AJAX calls to web services which is mainly what screws up my timing since it may still be trying to execute stuff when the redirect statement happens
}
catch (e) {
}
//Currently doing setTimeout(redirect, 10000);
//Would like to simply detect when all of the above is done and then redirect.
}
Edit: Left out a crucial piece of info. The AJAX calls are in a for loop. The use of variables and success callbacks hasn't been working so well for me as by the time my success callback is executing, my variables have taken on new values in the for loop.
What you are trying to achieve is a classical concurrent programming problem. It is solved by the use of a barrier.
To put it simply, you need to:
Count how many calls you've done.
Set a callback on all AJAX completion events.
Make that callback decrement the number of calls.
The callback checks whether the number of calls has reached zero or not. If yes, then your final code (here, redirect) is called.
The actual implementation is left as an exercise to the reader :)
Hint: embed AJAX calls into a function that handles all counter incrementation and callback setting.
What I do:
Create a variable that represents the number of outstanding AJAX calls.
Before making an AJAX call, increment the variable.
At the end of the code that completes an AJAX call, call a function (e.g. ajaxComplete).
ajaxComplete should decrement the count. When it reaches zero, you know all your calls are complete.
Assuming you're using jQuery.ajax, it sounds like you're looking for ajaxStop.
Why don't you try using something like the Underscore library's after function in the callbacks?
var done = _.after(3, function() {
window.location = 'http://example.com';
});
$.ajax({
url: '/tic',
success: function() {
done();
}
});
$.ajax({
url: '/tac',
success: function() {
done();
}
});
$.ajax({
url: '/toe',
success: function( data ) {
done();
}
});
You should check for the response from AJAX call, and only in that response do redirect. This way you will avoid doing redirect while AJAX was still executing.

Javascript and timing, specifically with callbacks

I want to make sure I understand callbacks properly, and javascript timing etc. in general.
Say my code looks like this, is it guaranteed to execute in order?
SetList(); // initializes the var _list
Some.Code(_list, function(data) {
// update list
});
DoSomething(_list); // operates on _list
Update
What I am seeing is SetList calls, then DoSomething, then Some.Code.
Some.Code calls another function. so:
Some.Code(_list, function() {
//load _list from ajax request
Other.Code.WithCallback(_list, function(){....});
});
I guess to fix this, I have to add DoSomething to the inner function as another callback?
SetList(), Some.Code() and DoSomething() will execute in that order, one after the other. The anonymous function passed as the second argument to Some.Code() could be called during the execution of Some.Code() (before the function returns and DoSomething() is called) or it could be called at a later time by another function, and event handler or timer, it all depends on when you specified it to be called.
Since you're using ajax, the request to the remote server is made on a separate thread, so the executing javascript thread continues to run and call other functions until a response (or, more specifically, for the onreadystatechange event to fire). When the ready state of the ajax request changes, its readystatechange event handler is queued to be called -- meaning it will execute as soon as all currently executing scripts finish.
If you want DoSomething() to execute after the response is received via ajax, you should run it to the end of your callback function instead.
That code would execute in order:
SetList(), then Some.Code(), then function(data), then DoSomething().
JavaScript is single-threaded, and executes in order. The only way that things would happen out of sync is if you set an interval/timer within Some.Code() or function(data) that called another function.
If you had:
var i=0;
functionCall() //some long process that sets i=1;
if (i==1) { alert("In Order!"); } else { alert("Out of Order!"); }
That would alert "In Order," But if you had:
var i=0;
setTimeout(functionCall, 1000) //some long process that sets i=1;
if (i==1) { alert("In Order!"); } else { alert("Out of Order!"); }
That would execute "Out of Order," because the third line would execute before functionCall() is called.
Updated Answer
Because you are using Ajax, I'm guessing you are making an asynchronous call, which is the reason for the delay. You have a callback function, but it's still waiting to be called back, so Javascript moves on to execute the next line while it waits.
To execute in the order you want, you'll need to do this:
SetList(); // initilizes the var _list
Some.Code(_list, function(data) {
// update list
DoSomething(_list); // operates on _list
});
This way, you can ensure that DoSomething() is called when your callback method is called, and not before.

Force Javascript function call to wait until previous one is finished

I have a simple Javascript function:
makeRequest();
It does a bunch of stuff and places a bunch of content into the DOM.
I make a few calls like so:
makeRequest('food');
makeRequest('shopping');
However, they both fire so quickly that they are stepping on each other's toes. Ultimately I need it to have the functionality of.
makeRequest('food');
wait....
makeRequest('shopping'); only if makeRequest('food') has finished
Thoughts on getting these to execute only one at a time?
Thanks!
If these functions actually do an AJAX request, you are better keeping them asynchronous. You can make a synchronous AJAX request but it will stop the browser from responding and lead to bad user experience.
If what you require if that these AJAX requests are made one after the other because they depend on each other, you should investigate your function to see if it provides a callback mechanism.
makeRequest('food', function()
{
// called when food request is done
makeRequest('shopping');
});
Using jQuery, it looks something like that
$.get("/food", function(food)
{
// do something with food
$.get("/shopping", function(shopping)
{
// do something with shopping
});
});
I would recommend that you simply write them asynchronously--for example, call makeRequest('shopping'); from the AJAX completion handler of the first call.
If you do not want to write your code asynchronously, see Javascript Strands
I suppose that you have a callback method that takes care of the response for the request? Once it has done that, let it make the next request.
Declare an array for the queue, and a flag to keep track of the status:
var queue = [], requestRunning = false;
In the makeRequest method:
if (requestRunning) {
queue.push(requestParameter);
} else {
requestRunning = true;
// do the request
}
In the callback method, after taking care of the response:
if (queue.length > 0) {
var requestParameter = queue.splice(0,1)[0];
// do the request
} else {
requestRunning = false;
}

Categories