I'm completely new in javascript and I'm trying to understand its asynch nature. For this purpose here is my sample code :
$("#calculate-similarity").click(function(){
// assume input is an array whose length is larger than 0
var requestData={"uris":input,"limit":100};
client=new Ajax(requestData);
alert('inside .click function');
})
Ajax=function(requestData){
alert('inside ajax');
$.ajax({
url: 'http://localhost:8080/',
type:'POST',
dataType: 'json',
contentType: 'application/json',
data: JSON.stringify(requestData),
xhrFields: {
withCredentials: true
}
}).done(function(data) {
$("#ws-results").children().detach();
$("#ws-results").append('<table id="my-final-table"><thead><th>fname</th><th>furi</th><th>sname</th><th>suri</th><th>similarity</th></thead><tbody></tbody></table>');
$('#my-final-table').dynatable({
dataset: {
records:data
}
});
});
}
Now, above, I'm creating new Ajax() and inside of it, I'm making a ajax request. As far as I know its asynch event. Therefore, I though that, this request should be completed first of all, and then my other javascript lines (alert('inside .click function')) should be executed. In other words, I would expect :
1) alert inside ajax
2) show my datatable on the browser
3) alert inside .click function
However, I got with the following order :
1) alert inside ajax
2) alert inside .click function
3) show table on the browser
So, what do you suggest me to understand these concepts ? I've a solid background with several programming languages like c++ and java but this is my first time with web development and javascript.
EDIT
If I modify my .click function like below, do you say first of all always 10000 times hello will be printed out and then table will be shown ? Or table would be shown somewhere at the middle of logging ? I mean when the response comes, engine should wait first in order to show it ?
Modified code : (Let's remove all of the alert statements)
$("#calculate-similarity").click(function(){
// assume input is an array whose length is larger than 0
var requestData={"uris":input,"limit":100};
client=new Ajax(requestData);
for(var z=0;z<10000;z++){
console.log(z+'hi!');
}
})
As far as I know its asynch event. Therefore, I though that, this request should be completed first of all, and then my other javascript lines should be executed.
That is exactly the opposite of what it means.
The Ajax function will run. It will trigger an HTTP request. The Ajax function will finish. alert will run.
At some point in the future, the HTTP response will arrive and the done event handler will fire.
This is exactly the same principle as:
alert(1);
$("#calculate-similarity").click(function(){ alert(2); });
alert(3);
JavaScript doesn't wait for you to click on calculate-similarity before firing alert(3).
If I modify my .click function like below, do you say first of all always 10000 times hello will be printed out and then table will be shown ? Or table would be shown somewhere at the middle of logging ? I mean when the response comes, engine should wait first in order to show it ?
JavaScript won't interrupt a running function in order to execute a different (event handler) function. It will wait until it isn't busy before it goes looking for events.
new Ajax is object instantiation and it's synchronous. Therefore you get inside ajax as the first result because it happens when your Ajax object is instantiated, not to be confused with when the Ajax request is fired.
alert is executed synchronously, so that's the second thing you get.
$.ajax which wraps around XMLHttpRequest, responsible for firing the actual ajax request, is the only async part in your code and its result, which is encapsulated inside done, is what you get last.
In other words, I think the confusion comes from the fact that you introduce another layer of abstraction called new Ajax() which provide little actual value and a lot of confusion :P. inside ajax signal inside the instantiation of your Ajax object, not the firing of the actual request.
I'll try my best to explain it. Think of this more as an analogy, it's not exactly what's going on but I think it might help you understand:
alert('inside ajax'); - this is a blocking call, it will run and wait for you to click OK.
Then when you call Ajax, what you're essentially doing is saying "go make this web request when you have a chance, and when it finishes call my done method." That's a network operation. It could finish in 1 second, it could take many seconds. Rather than freezing up the whole browser, this is done "in the background." So that makes the UI remains responsive. At some point in the future the network request will finish. When it does, the function you specified in done will get called to let you know it finished. Think of making the Ajax request as adding it to a queue rather than actually connecting to the network. When the browser gets to it it will execute your request and wait for the server to respond. When it does, it will signal you to let you know.
Next you alert('inside .click function'); which displays the alert and blocks.
Like I said, that's not a technically accurate description of what's going on, but I'm hoping it helps you understand the principle of it.
Related
I have written a simple web page where I would like to be able to execute concurrent ajax requests. I know that I can do concurrent ajax requests in jquery using .when() but that's not exactly my case. I have a function like the following:
function getData(tt, tf) {
$.ajax({
url : "/extpage.php",
type : "POST",
async: true,
data : {
testt : tt,
testf : tf
}
})
.done(function (toolbox) {
alert(data);
});
}
This function is called from a button inside the webpage and I need to be able to let the user call this function anytime he wants (I'm aware about the maximum number of the ajax requests that a browser can support) without waiting the previous ajax request to be finished first and then execute the next one. I want every call to be processed in parallel. Any clues on how I can obtain that ?
That's how AJAX works inherently. Each call you perform is run independent of any other browser activity (including, generally, other AJAX calls).
Given the function you have, if I call getData() ten times in a row, it will initiate ten independent HTTP requests. If they're not running concurrently it is possible that the server simply won't answer more than one request at a time, and of course you can't do anything about that.
I think you may need to revise your question.
This function is called from a button inside the webpage and I need to
be able to let the user call this function anytime he want
This is the default AJAX behaviour. AJAX calls are ansychronous.
async: true
is redundant, true is the default value for async.
Your code should do what you are asking in this question, if you are still experiencing a problem the issue may be elsewhere.
As one last note:
$.when()
is used to queue otherwise concurrent/async tasks, the opposite of what you suggested in the OP.
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.
I'm adding some functionality to an existing function. I need to insert an additional step in the middle of the current routine. I know how to go to the 2nd function but I don't know what to do to return to the main function once the 2nd routine completes.
function step1(){
perform ajax call to see if student is assigned to a project
step1_subfunction()
// wait here until step1_subfunction is done
do some more stuff with response from user
}
function step1_subfunction(){
prompt user via jQuery dialog, 'Add or move employee to the project?'
// return to step1 with answer returned from user and resume
}
I'd google this but I don't know if this "process" has a name. Back in my days of COBOL, we called this gosub.
UPDATED:
Step1 performs an ajax call to see if an employee has been assigned to a project. If the response.status = 'Assigned', the user will be asked via a jQuery dialog box, "Do you want to copy or move the employee to this project?". The jQuery dialog box will be step1_subroutine. The answer will be passed back to the step1 function. The remaining part of step1 will simply be to place a value in a hidden text field of "copy" or "move".
What you have will perform what you are describing, but may not make the data from the user available to function step1() without a return in function step1_subfunction(). Below I've modified your example code to demonstrate the passing of values back.
function step1(){
//do some stuff
var returnValFromFunction = step1_subfunction();
// wait here until step1_subfunction is done
// Now use returnValFromFunction, it contains the information from the user
do some more stuff with response from user
}
function step1_subfunction(){
prompt user for some information
// return to step1 with information returned from user and resume
return userResponse;
}
What you've written should work - javascript is single-threaded, so have you tried it?
Javascript doesn't have subroutines specifically, just create a function that returns and ignore the result, as you have done. When the second routine completes, the scope and execution will continue in the first function.
just do nothing.
what you are trying to achieve is just a "function call"
so it will automatically return to it's caller "stack frame" once executed.
You can make the ajax object synchronous - IE, no code will continue until it gets a response. It's the third parameter of open (true is asynchronous, false is synchronous).
xmlhttpobject.open('POST', 'url', false);
There are cases where a synchronous call is fine but it should always be avoided if possible.
The other alternative, which would likely require some logic changes in your code but would be better off in the long run, is to bind the onReadyStateChange event. This fires every time the state of the xmlHttpRequest object changes - you can check to see if the status is 200 and the readystate is 4 to make sure the request is done and completed successfully.
Here's a better reference. Good luck.
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.
I have a function called:
function callAjax(url, data) {
$.ajax(
{
url: url, // same domain
data: data,
cache: false,
async: false, // use sync results
beforeSend: function() {
// show loading indicator
},
success: function() {
// remove loading indicator
}
}
);
}
In the code, I call "callAjax" X number of times and I want to update the data synchronously. It is done as expected, but one problem: the loading item doesn't show in beforeSend function. If I turn async to true, it works but the updates aren't synchronously done.
I've tried several things with no success. I tried putting the loading indicator before the ajax call like this:
function callAjax(url, data) {
// show loading div
$.ajax(
{
// same as above
}
);
}
But for some reason it doesn't want to show the loading indicator. I notice a strange behavior when I put an "alert" in the beforeSend and the loading indicator appears in that case, but I rather not pop up a message box.
Got any ideas?
Making a synchronous call like that is like putting up an "alert()" box. Some browsers stop what they're doing, completely, until the HTTP response is received.
Thus in your code, after your call to the "$.ajax()" function begins, nothing happens until the response is received, and the next thing as far as your code goes will be the "success" handler.
Generally, unless you're really confident in your server, it's a much better idea to use asynchronous calls. When you do it that way, the browser immediately returns to its work and simply listens in the background for the HTTP response. When the response arrives, your success handler will be invoked.
When you do the blocking I/O the program is halted until the the input is received, in JS words when doing a synchronous call, the program halts and browser window freezes (no painting can be done) until the response is received. In most cases doing syncronus calls and any kind of blocking I/O can be avoided. However imagine your doing a progress bar in java or any other programming language, you have to spawn a different thread to control the progress bar, I think.
One thing to try in your case, is to call the ajax call after a time delay
//loading div stuff,
//if your doing some animation here make sure to have Sufficient
//time for it. If its just a regular show then use a time delay of 100-200
setTimeout( ajaxCall, 500 );
EDIT ajaxcall in setTimeout, Example
This is what you are looking for - .ajaxStart()
It will be triggered when any ajax event starts
http://api.jquery.com/ajaxStart/
They even give a specific example similar to what you are trying to accomplish:
$("#loading").ajaxStart(function(){
$(this).show();
});
You can then use the .ajaxStop() function
$("#loading").ajaxStop(function(){
$(this).hide();
});