Suppose I have this code:
$('#button').on('click', function () {
$('#status').text('doing some work....');
somethingThatTakes20Seconds('#status');
});
Here, somethingThatTakes20Seconds will be executed before the "doing some work" statement. While I understand the statement itself creates a DOM event which gets placed into the event queue and waits until the stack is clear to execute, what I don't get is how it's doing it (on a high level). Is the .text method asynchronous, in the same way like setTimeout() is (just with .text you don't take any callback, or is the callback auto-generated, basically some code that updates the DOM)?
The text() method is not asynchronous. $('#status').text('doing some work....'); will execute before somethingThatTakes20Seconds('#status');.
You can define your somethingThatTakes20Seconds() method to register a callback - which internally adds a listener to the JavaScript engine. When the listener "hears" something - ie. an AJAX request is completely or an user performs an action, it adds an item to the message queue.
This is where the event loop comes in. The event loop takes this message queue item and then calls the callback function associated with it.
No, .text() method is not asynchronous.
JavaScript only executes your code each line in sequence. It does not wait until each line execution is completed to execute the next line.
In your code, basically what you're doing is:
Change the #status text to doing some work.....
Run somethingThatTakes20Seconds() function.
It's just impossible for your function to do the 2nd function for 20 seconds, and then run the 1st one considering how fast it is to run the 1st function.
Consider the following sample,
function addTextOne() {
$('#result').append('This is text 1.\n');
}
function addTextTwo() {
$('#result').append('This is text 2.\n');
}
$(function() {
$("#testBtn").on('click', function() {
setTimeout(addTextOne, 1001);
setTimeout(addTextTwo, 1000);
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button id="testBtn">
Test
</button>
<br/>
<textarea id="result" cols='70' rows='30'></textarea>
Note: The code sample basically executes addTextOne before addTextTwo on button click. But, addTextTwo finishes earlier than addTextOne.
The browser will not render the DOM to show the "doing some work..." message until after the function returns. Assuming your somethingThatTakes20Seconds function does not return for 20 seconds, you will not see that message for 20 seconds. You probably want to do something like this:
$('#button').on('click', function () {
$('#status').text('doing some work....');
setTimeout(function () { somethingThatTakes20Seconds('#status'); }, 10);
});
This will allow the DOM to be rendered before starting the long-running process.
For example: https://jsfiddle.net/9eq85udz/2/
Related
setTimeout(function(){
console.log("m");
}, 0);
console.log("s");
Why does this code print "s" before "m", even if the setTimeout callback is supposed to wait for 0 ms?
A browser or node.js always run a single threaded event loop to run your code. On the first run it will always run your synchronous code but may also que up asynchronous events that will call back later. Thats why we call the function here callback function it will be called later.
setTimeout is a microtask.
That means the function that you see isnt gona executed immedantly, it is gonna first queued up and will be executed within the next event loop.
Also a sidefact: 0 ms just means it will minimum wait 0 ms not exact 0
When you create a promise, or call an async function, or set a timeout for 0 milliseconds, the function is immediately queued into the Javascript event loop. Essentially, the function is added to a queue of functions to call, and once the javascript interpreter has nothing to do it'll start calling those functions. So, when you set a timeout for 0 milliseconds, it queues the console.log("m"), then calls the console.log("s"), then it has nothing to do so it finishes the queued console.log("m"), which is why it's out of order.
it just because JS is single-threaded and event loop works that way.
setTimeout has written in a way that it will send you function or whatever you want to do in a callback queue.
and then move forward to the next line, once next line executed it will not run your setTimeout part, or in other words, it will not process the setTimeout part until the stack is not empty.
so this is your code, and it will execute like this.
setTimeout(function () {
console.log("m");
} , 0)
console.log('s');
the first line will execute and it will send the inner part of setTimeout to callback queue and move to the 2nd line.
while 2nd line is executing the setTimeout part will wait till the stack is not emplty and as soon as 2nd line finishes execution,
the setTimeout part will execute,
maybe it's confusing by words, let's see this in action. I bet you can not get a better example than this to understand it, it's explained in the best way by Philip robert.
because JS code goes in order one by one. When you specifying setTimeout to 0 is still waiting, in C++ lang this would be something like this 0.000000245ms, and JS runs often on C++/C browser.
try this simple example
for (let x = 0; x < 500; x++) {
setTimeout(() => console.log(x), 0);
}
console.log('hello');
lets imagine a scenario
function clickEventCheck() {
document.querySelector('#some-id').addEventListener('click', () => {
console.log("The button is clicked");
});
}
clickEventCheck();
Now I get that for the first time the 'clickEventCheck' function is getting called and we can handle the click event.
But now the execution stack is empty so how the event handler line of code which is inside our function(which has returned) still gets executed every time we click the button?
When the function is called click listener is added to the element with id some-id. So, whenever you click the element, the code inside the callback function gets executed.
Until and unless the click listener is not explicitly removed from the element i:e element with id some-id, it will listen to click events.
function clickHandler() {
console.log('The button is clicked');
}
function clickEventCheck() {
document.querySelector('#some-id').addEventListener('click', clickHandler);
}
clickEventCheck();
document.querySelector('.remove').addEventListener('click', () => {
document.querySelector('#some-id').removeEventListener('click', clickHandler);
});
<button id="some-id">Click Me</button>
<button class="remove">Remove Click Listener</button>
JavaScript is doing more than just executing your code sequentially, line by line. In the background there is also something running called the event loop. This frequently checks another stack to see if any other instructions are there and if so runs those, starting additional chains of execution called frames.
Two nice properties of those frames is that they will keep running as long as they can so you don't have to reason about random switching between them, and they don't block, so once a frame can't execute any further, the engine will look for another frame to execute from the stack. This also leads to smooth switching between frames provided you are not doing CPU-heavy execution (which isn't normally what JavaScript's used for).
Practically what might happen is one frame (frame A) makes an I/O request, and provides a callback function when it does so. Frame A then runs out of code to execute and the event loop picks the next frame (frame B) to run. In the background, when the I/O request completes the callback function will be added to the stackāand then when frame B runs out of code, the event loop can pick up that callback and execute it in yet another frame. The same process could apply for a button click, a mouse move, or any asynchronous process you can get the computer to do.
In this way a large number of I/O connections can be smoothly handled simultaneously and this is a big selling point of NodeJs.
A lot of callbacks can get messy quickly and that's why there also exists concepts like Promises and async functions, which are topics for another day.
Edit: I apologize for any confusion. In my code, a loop is running in the script indefinitely until a certain condition is reached. My question is, if an event listener calls a function while this loop is running, after the completion of the function, where would execution continue?
Well, that's pretty much self-explanatory, I guess. After a function is called by an event listener, where does execution of code continue after the function finishes?
There is an event queue, and whatever is next in that queue gets executed. It could for example be a mouse click event, a window resize or a time-out. If there is nothing on the queue, the container, in which JavaScript runs, will just loop until there is something in the queue to process.
You can read more in MDN's article on "Concurrency model and Event Loop":
A JavaScript runtime contains a message queue, which is a list of messages to be processed. To each message is associated a function. When the stack is empty, a message is taken out of the queue and processed. The processing consists of calling the associated function (and thus creating an initial stack frame). The message processing ends when the stack becomes empty again.
A common way to put something on the message queue is by calling
setTimeout(myfunction, 0);
As the delay in the second argument is 0, the function myfunction will be called once the currently executing code completes, i.e. the call stack becomes empty. Note however, that it could be that some other events were already on the queue. In that case those will still be executed first.
What with a long-lasting loop?
You added the following to your question:
In my code, a loop is running in the script indefinitely until a certain condition is reached. My question is, if an event listener calls a function while this loop is running, after the completion of the function, where would execution continue?
If your loop does not run via asynchronous calls (e.g. with repeated calls to setTimeout or with setInterval), but looks like this:
while (!endingCondition) {
// do nothing
}
then there is no way another (JavaScript) event listener gets called. Such an event will be waiting on the message queue. Only when your current loop and any code after it finishes, will that event be processed and result in the call of the event listener.
So let's look at the following example:
var clicked = false;
document.onclick = function() {
clicked = true;
}
while (!clicked) {};
alert('clicked!');
Here one might hope that the while loop gets interrupted by a click and shows the alert, but that is not true. The click event will be in the operating system's message queue, but will not be consumed by JavaScript because it always first completes the code it is currently executing. As stated in the above-mentioned article on MDN:
whenever a function runs, it cannot be pre-empted and will run entirely before any other code runs.
That any code includes all JavaScript code, including code in event handlers.
How to write "interruptible" code
The above example can be made to work like this:
var clicked = false;
document.onclick = function() {
clicked = true;
}
function detectClick() {
if (!clicked) {
setTimeout(detectClick, 0); // keep repeating
return;
}
alert('clicked!');
// do something more ...
};
detectClick(); // call for first time
// We get here immediately. Code will end, and events will be processed.
// One of those "events" is a time-out, to which we have set a handler: detectClick.
// At some point a click event will be there as well, triggering the other handler.
Ok, I am pretty new to jquery and Javascript.
I was reading about callback on w3school and it gives two examples.
Example1:
$("button").click(function(){
$("p").hide("slow",function(){
alert("The paragraph is now hidden");
});
});
Example2:
$("button").click(function(){
$("p").hide(1000);
alert("The paragraph is now hidden");
});
I understand that in first case alert will ONLY be executed after hide() function has finished.
However in second example it is possible that alert might execute before hide finishes.
This has caused some confusion in my understanding. Like for example is it possible that alert('hey') might get executed before alert which comes before it (one with mathmatical calculation) in following case..
$("button").click(function(){
alert(1+2+(3*4)..blah..blah);
alert('hey');
});
OR
in this case..
$("button").click(function(){
fn1();
fn2();
});
function fn1(){
for(var i=0;i<100;i++){
$('table').append(blah);
}
}
function fn2(){
alert('hey');
}
Is it possible that 'hey' might appear before fn1 has finished appending?
If so do I need to write every thing as callback??
To answer your question: No.
The key is that certain javascript functions are asynchronous. These really only come in two common categories:
XmlHttpRequest (i.e. AJAX) calls, where the browser goes out to the network to get something, and it lets the script continue running while it gathers the response.
timeouts and intervals, where you tell the browser to call some code after a delay. The script will continue unimpeded and then, when the time arises, the timeout code will run.
In your examples:
$("p").hide("slow",function(){
alert("The paragraph is now hidden");
});
The hide function in jQuery is timeout based. So your script does not have to wait for the animation to complete before it gets on with its business. jQuery provides a callback parameter so you can choose to have something happen after the animation completes if you want.
So in this example:
$("button").click(function(){
$("p").hide(1000);
alert("The paragraph is now hidden");
});
It is wrong to say the alert "might" execute before the hide finishes. Unless your code is executing so slowly that it takes more than 1 full second for an alert to show, it will execute before the hide completes. 1000ms is an eternity to a line of javascript.
$("button").click(function(){
alert(1+2+(3*4)..blah..blah);
alert('hey');
});
In this example, there is nothing asynchronous about the code. alert is a so called blocking call, meaning nothing happens in the script until you dismiss the alert. So you are guaranteed that the alerts will appear in order no mater how complex you make the parameter.
In fact, the complexity of the parameter has no bearing because it will evaluate in full before the resulting string is passed to the alert function.
So long story short, unless you're doing Ajax, setTimeout and setInterval, or using a third party library (which should document its behavior) your code will execute in order.
No. The reason the alert() occurs first in example 2 is because the hide() call is asynchronous. The hide() function is fired, but this has a 1000 millisecond delay. The alert() is fired instantly afterwards, not 1000 milliseconds afterwards, therefore it appears that the alert() was fired first.
In example 1 the alert() fires only when the hide() has completed, as this uses a callback function.
When using alert or confirm in Javascript, the browser is forced into a synchronous (existing or occurring at the same time) process where everything (even the loading of another page) halts until the user dismisses the dialog.
So when you alert something, browser will halt execution of other functions.
But jQuery hide and other animation functions are asynchronous (not existing at the same time) so that browser will go to next line without waiting for them.
For ex.
$(document).ready(function(){
fn1();
fn2();
});
function fn1(){
for(var i=0;i<100;i++){
$('body').append("<div>blah</div>");
console.log("blah!");
}
}
function fn2(){
console.log("Hey!");
}
Here hey will be logged after blah (100 times) as the browser waits for f1() to complete.
DEMO
But if you try something like:
$(document).ready(function(){
fn1();
fn2();
});
function fn1(){
for(var i=0;i<100;i++){
if(i%10==0)
{
fn2();
alert(true);
}
console.log("blah!");
}
}
function fn2(){
console.log("Hey!");
}
Then alert will show its way of working.
Also in jQuery:
$("p").hide("slow",function(){
// This is the call back function and not others.
});
The callback will be executed when any async or sync function first of all executes its tasks.
no it is not possible, for functions the JavaScript does them line by line but all in once, BUT it returns the result of the first one after giving the result of the second one! but as for the other example, it is obvious that hide() is going to take much longer to give the requested respond comparing to alert which is a browser built in function and that's why the alert appears to be working before hide(), I don't know the exact time that it takes to do these things but if you google it, you can learn them too if you need to!
BTW when an alert() pops up, it shuts down the whole javascript codes while it's on, just for you to know. ;)
I was goofing around with JavaScript, and a notice a strange behavior (strange for me at least. . .)
So I did a SSCCE here it goes:
I have a div named "myDiv"
function changeText(text){
document.getElementById("myDiv").innerHTML=text;
}
function recursiveCall(counter){
if(counter){
setTimeout(function(){
recursiveCall(--counter);
changeText(counter);
},750);
}
}
recursiveCall(10);
Live example: http://jsfiddle.net/T645X/
So I'm changing the text on the div, and what happens is that the text goes from 9 to 0, while I thought that it was suppose to go from 0 to 9, since the recursive changeText(counter); call is before calling the method that actually changes the text.
The function contains a timeout which is asynchronous.
setTimeout(function(){
recursiveCall(--counter);// calls the next function, which will call the next
// and print in a timeout
changeText(counter); // print
},750);
The text is changed before the recursive call hits the timeout.
If you'd like to, you can move the print call from outside the timeout, which would result in the expected behavior as such:
function recursiveCall(counter){
if(counter){
recursiveCall(--counter);
setTimeout(function(){
changeText(counter);
},750);
}
}
(Although, note that here the printing is not timed apart, and we're relying somewhat on undefined behavior assuming it'd print first just because we put the timer first)
If you would like it to still print in delays, you can tell the function it's done. Recursion will still be done initially, but each level will tell the level above it that it is done:
function recursiveCall(counter,done){
if(counter){
// note how recursion is done before the timeouts
recursiveCall(counter-1,function(){ //note the function
setTimeout(function(){ //When I'm done, change the text and let the
changeText(counter-1); //next one know it's its turn.
done(); // notify the next in line.
},750);
});
}else{
done(); //If I'm the end condition, start working.
}
}
Here is a fiddle implementing this.
Strictly speaking there is no recursion here.
The call to setTimeout just adds a callback to a list of scheduled timer events.
Much of the time, your browser is just sat there waiting for events, it processes those (i.e. runs your event handlers) and then goes back to waiting for events.
So in this case what you're doing is:
recursiveCall(10)
timer event and callback added to the queue
function exits
... waits 750 ms ...
timer event fires, callback pulled from the queue and invoked
-> recursiveCall(9) invoked
-> timer event and callback added to the queue
-> changeText(9) invoked
callback function exits
... waits 750 ms ...
timer event fires, callback pulled from the queue and invoked
-> recursiveCall(8) invoked
-> timer event and callback added to the queue
-> changeText(8) invoked
callback function exits
and so on...
I call this pseudo-recursion, because although it looks somewhat like classical recursion, each invocation starts at the same "stack frame", i.e. if you asked for a stack trace there would typically only be one (or in your case sometimes two) instance of recursiveCall present at a time.
One thing to understand is that it's not recursion in the first place; if your function didn't have a proper exit clause, this could go on forever without running into a blown stack.
The reason is that any function you pass to setTimeout() is run outside of the current execution context; in other words, the code "breaks out" of your function.
If you want a recursive call with 750ms in between them, you could do something like this:
function recursiveCall(counter, fn)
{
if (counter) {
recursiveCall(--counter, function() {
changeText(counter);
setTimeout(fn, 750);
});
} else if (fn) {
fn(); // start chain backwards
}
}
It creates a chain of callbacks when it recurses and the exit clause sets the whole chain motion, backwards :)