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
Related
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.
My code is structured as follows:
IF (something) {
..stuff
..Asynchronous Function Call
}
ELSE (something) {
..stuff
..Asynchronous Function Call
}
..more stuff
Let's say the IF condition is met, the code executes 'stuff', then moves onto the Asynchronous Function Call. Will it simple do the call but get out of the IF statement and execute 'more stuff' in the mean time on the assumption of waiting for the Asynchronous Function Call to finish?
OR
Does it finish waiting for the Asynchronous Function Call to finish executing, then continue with 'more stuff' as a normal IF statement block would do.
In the prior case, any advice on how to ensure the Asynchronous Function Call finished before it exits the IF block?
** Note, I've included more stuff inside both Asynchronous Function Calls to ensure the calls are done before it moves on, but I feel this is really bad programming because if I had 50 ELIF's, I would have to copy paste that code 50 times as opposed to just putting it at the end of the IF statement.
Thank you very much for any help provided!
You can approach this easily and less painfully using JavaScript Promises. Have a look to the following links:
http://davidwalsh.name/write-javascript-promises
https://www.promisejs.org/
The basic idea of JavaScript Promises is to the use of asynchronous calls that can be executed in a certain order. Like this:
$.when(GET_PRODUCTS).then(
IF_SUCCESS DO THIS
ELSE DO THAT
).fail(
SHOW MESSAGE
CLEAN EVERYTHING BECAUSE SOMETHING WRONG HAPPENED
).done(
CLEAN EVERYTHING BECAUSE EVERYTHING WENT OKAY
)
With that, you can make code that will be more maintainable. It is not easy to grasp it at the beginning, but give it a try, will save you a lot of headaches!
Does it finish waiting for the Asynchronous Function Call to finish executing,
No, that isn't what "asynchronous" means. The while point is that it doesn't wait. The function will run and finish at some point in the future; the flow of execution continues to the next line immediately.
In regards to your given code, more stuff happens while the asynchronous function is happening. It doesn't wait for the asynchronous function to return a result.
Based on your tag of "node.js", I'm assuming your question is about asynchronous calls on the server. However, you can compare the behavior to a client-side AJAX call.
Say you have this:
var nav = document.getElementById('nav');
function async(params) {
var xhr = new XMLHttpRequest();
// set up your request
xhr.onreadystatechange = function() {
// some conditions, and then on success:
nav.style.color = 'black';
};
xhr.open('GET', 'resource.php'+params, true);
// send your request
}
if ( /* condition */ ) {
async( /* some parameter */ );
} else {
nav.style.color = 'red';
}
If you were to run the above code, either way, your #nav element's color will be set to red at first, but if the async request comes back with a successful response, your #nav element's color will be black. This is a very trivial and probably impractical example, but it is one that could be tested pretty easily to confirm that yes, async calls will happen asynchronously.
Like others of said you can use Promises, async.js, step.js, etc. To control flow. You can also use generators if you use latest version of node with --harmony enabled.
I promise to show you the right way. First off asynchronous if conditions are what you're looking for. Secondly you want a full example. You'll have to modify the URL of the AJAX request and set some server code to give you responses. I'll provide a baseline PHP file towards the end.
So effectively: what if my if condition takes too long for the JavaScript parser? JavaScript uses promises. I'm not going to go all-out crazy, my goal here is to provide a baseline. Towards the end of the script you'll notice either success or failure levels. This script requires two asynchronous if conditions. Additionally instead of being cheap/static/fragile I've kept the script element within the head element where it belongs. Lastly ensure you change the HTTP query and acknowledge it at the server, no need to produce redundant files. Browser compatibility is good except no support for IE11 however I've only encountered very specific use-cases to require this so if you're considering using this code for non-technical audience I would highly recommend reconsider your initial approach to the given problem.
<head>
<script defer="true" type="application/javascript">
//<![CDATA[
function request(method,url)
{
return new Promise(function(resolve, reject)
{
var req = new XMLHttpRequest();
req.open(method,url);
req.withCredentials = true;
req.onerror = function() {reject(Error('Network error.'));};
req.onload = function() {if (req.status == 200) {resolve(req.response);} else {reject(Error(new Object({'response':req.response,'status':req.statusText})));}};
req.send();
});
}
function aysn_if()
{
request('get','https://www.example.com/test.php?t=1').then(function(response)
{
console.log('Success, level one if!', response);
request('get','https://www.example.com/test.php?t=2').then(function(response)
{
console.log('Success, level two if!', response);
},
function(error)
{
console.error('Failed, second level.', error);
});
},
function(error)
{
console.error('Failed, first level.', error);
});
}
document.addEventListener('DOMContentLoaded', function (e) {aysn_if();},false);
//]]>
</script>
</head>
PHP
<?php
header('Access-Control-Allow-Credentials: true');
header('Access-Control-Allow-Origin: '.((isset($_SERVER['HTTP_ORIGIN'])) ? $_SERVER['HTTP_ORIGIN'] : '*'));
//JUST for testing, don't send this stuff outside of test environments!
ksort($_SERVER);
print_r($_SERVER);
?>
You first option is what happens.
You don't have to copy/paste N times. Just put "more stuff" into a function, and pass that function to all your asynchronous callbacks. The callbacks can just call the "more stuff" function when they are done with their normal processing.
I am giving my first steps in Javascript and trying to understand how it works.
I've come to a problem of execution order of the code.
var Parsed = [[]]
var txtFile = new XMLHttpRequest();
alert("Trying to open file!");
txtFile.open("GET", "http://foo/f2/statistics/nServsDistrito.txt", false);
txtFile.onreadystatechange = function() {
if (txtFile.readyState === 4) { // Makes sure the document is ready to parse.
if (txtFile.status === 200) { // Makes sure it's found the file.
alert("File Open");
allText = txtFile.responseText;
Parsed = CSVToArray(allText, ",")
}
}
}
txtFile.send(null);
alert("Job Done");
The problem is "Job Done" is appearing first than "File Open".
But the file has information necessary for code following the "Job Done" alert.
I changed the asynchronous part of the "get" request but didn't work.
What can i do to stand by all code while the file is open and the information retrieved?
Can i use the readyState to stall the code while the file is being opened and parsed?
Thanks for the help.
Update: It now works thanks to all.
XMLHttpRequest is an async operation. It doesn't matter whether your file is readily available or even if there is no networking involved. Because it is an async operation, it will always execute after after any sequential/synchronous code. That's why you have to declare a callback function (onreadystatechange) that will be called when open comes back with the file contents.
By the explanation above, your code in this example wouldn't be correct. The alert line will be executed immediately, not waiting for the file contents to be ready. The job will only be done when onreadystatechange has finished executing, so you would have to put the alert at the end of onreadystatechange.
Another very common way to trigger async operations is by using setTimeout, which forces its callback function to be executed asynchronously. Check out how it works here.
Edit: You are indeed forcing the request to be synchronous by setting the third parameter to open to false (https://developer.mozilla.org/en-US/docs/DOM/XMLHttpRequest#open()). There are very few cases in which you want a request like that to be synchronous, though. Consider whether you need it to be synchronous, because you will be blocking your whole application or website until the file has been read.
That's because you are using asynchronous functions. When working with async functions you have to use callbacks.
A callback is a function (eg. function cback()) you pass as parameter to another function (eg function async()). Well, cback will be used by async when necessary.
For example, if you are doing IO operations like reading files or executing SQL queries, the callback can be used to handle the data once retrieved:
asyncOperation("SELECT * FROM stackoverflow.unicorns", function(unicorns) {
for(var i=0; i<unicorns.length; i++) {
alert("Unicorn! "+unicorns[i].name);
}
});
The anonymous function we are giving asyncOperation as the second parameter is the "callback", and it's going to be executed once the query data is ready. But while that operation is being handled your script is not blocked, this means that if we add this line after the previous code:
alert("We are not blocked muahahaha");
That alert will be shown before the query is completed and unicorns appear.
So, if you want to do something after the async task finishes, add that code inside the callback:
asyncOperation("SELECT * FROM stackoverflow.unicorns", function(unicorns) {
for(var i=0; i<unicorns.length; i++) {
alert("Unicorn! "+unicorns[i].name);
}
//add here your code, so that it's not executed until the query is ready
});
Note: as #radhakrishna pointed in a comment the open() function can also work in a synchronous manner if you pass true instead of false. This way the code will work as you were expecting: line after line, in other words: synchronously.
Callbacks can be used for a lot of things, for example:
function handleData(unicorns) {
//handle data... check if unicorns are purple
}
function queryError(error) {
alert("Error: "+error);
}
asyncOperation("SELECT * FROM stackoverflow.unicorns", handleData, queryError);
Here we are using two callbacks, one for handling the data and another one if an error occurs (of course that depends on how asyncOperation works, each async task has it's own callbacks).
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;};
}
I have an Ajax call that currently needs to be synchronous. However, while this Ajax call is executing, the browser interface freezes, until the call returns. In cases of timeout, this can freeze the browser for a significant period of time.
Is there any way to get the browser (any browser) to refresh the user interface, but not execute any Javascript? Ideally it would be some command like window.update(), which would let the user interface thread refresh.
If this would be possible, then I could replace the synchronous AJAX call with something like:
obj = do_async_ajax_call();
while (!obj.hasReturned()) {
window.update();
}
// synchronous call can resume
The reason that I can't use setTimeout, or resume a function in the callback, is that the execution flow cannot be interrupted: (there are far too many state variables that all depend on each other, and the long_function() flow would otherwise have to be resumed somehow):
function long_function() {
// lots of code, reads/writes variable 'a', 'b', ...
if (sync_call_is_true()) {
// lots of code, reads/writes variable 'a', 'b', ...
} else {
// lots of code, reads/writes variable 'a', 'b', ...
}
// lots of code, reads/writes variable 'a', 'b', ...
return calculated_value;
}
You need to replace your synchronous request with an asynchronous request and use a callback. An oversimplified example would be:
obj = do_async_ajax_call(function (data, success)
{
if (success)
{
// continue...
}
});
function do_async_ajax_call(callback)
{
var xhr = new XMLHttpRequest();
xhr.open("GET", "http://mysite.com", true);
xhr.onreadystatechange = function ()
{
if (xhr.readyState == 4 && xhr.status == 200)
callback(xhr.responseXML, true);
else if (xhr.readyState == 4)
callback(null, false);
}
xhr.send();
}
This way you're passing an anonymous function as a parameter to the ajax requesting function. When the ajax is complete, the function that was passed is called with the responseXML passed to it. In the meantime, the browser has been free to do it's usual thing until the call completes. From here, the rest of your code continues.
Take the rest of the call and put it in the callback that is called when the result comes back. I seriously doubt that this would be completely impossible for you to do. Any logic you need to put in the call can be duplicated in the callback
asynchronous ajax fetch then settimeout and do the processing work in chunks (triggered by the callback)
JavaScript is single-thread. So by definition, you cannot update UI while you are in a tide loop. However, starting from Firefox 3.5 there's added support for multi-threaded JavaScripts called web workers. Web workers can't affect UI of the page, but they will not block the updates of the UI either. We workers are also supported by Chrome and Safari.
Problem is, that even if you move your AJAX call into background thread and wait of execution to complete on it, users will be able to press buttons and change values on your UI (and as far as I understand, that's what you are trying to avoid). The only thing I can suggest to prevent users for causing any changes is a spinner that will block the entire UI and will not allow any interaction with the page until the web-call returns.