Javascript is a synchronous language by default. This means that all the statements and functions execute one after the other in a predefined order. Javascript behaves this way because it has only a single thread of execution. Other languages like Java provide a multi-threaded execution environment, wherein there’s the main thread as well as other threads that can be created in runtime to run tasks in parallel. Hence, asynchronicity is quite simple and straightforward to achieve in these languages.
However, since Javascript provides us only with a single thread of execution, we need to understand how certain functions that seem to be asynchronous, like the setTimeout function, are able to run. But before we do that, let’s have a look at how the single-threaded execution flow works.
Single-Threaded Execution
Take the example of this simple program
Output:
1
2
In line 1, the program saves a function declaration to a variable ‘sayOne’. Note that it only saves the function declaration but does not call it yet. So, at this point, none of its code is actually run and hence line 2 would not get executed yet. In line 5, it saves another function definition to a variable ‘sayTwo’ (but doesn’t call it yet). In line 9, it calls the function sayOne. At this point, the saved function definition of sayOne is executed, which results in line 2 of the program being executed and the value of “1” being printed onto the console. Similarly, in line10, the program calls the function sayTwo, which leads to line 6 getting executed, which prints the value of “2” onto the console.
The above execution flow seems pretty straightforward and easy to grasp. Javascript executes the program line by line and executes them in that order. However, as you may have seen, the program isn’t truly being executed line by line and there’s some jumping around in the order of execution of lines, due to function calls. We’ll see about it later in this article. Another good thing to note here is that Javascript wouldn’t move on to the next line of execution until the previous line is executed.
For example, let us assume the sayOne function had a complex code that took a lot of time to execute (for example, a second). In this case, when in line 9, the program would wait till the sayOne function is completely executed before moving on to line 10 to execute the sayTwo function there. This is because, as we pointed out earlier, Javascript runs on a single thread of execution which is shared by all functions. Hence, the program waits until the current function is completely executed before moving on.
Let us tweak the above code a bit and try to understand what happens then. Let us add a third function called ‘sayThree’.
Let us now call this function in the code
As you can see, we have called the sayThree function (which prints “3” onto the console) inside the body of function sayOne. Hence, in the execution of the above program, what do you think would happen in line 14 when function sayOne is called? Would the console first log “1” and then move on to executing the code of sayThree, or would it pause its own execution, call the sayThree function, wait for it to complete, and then move on to finally print “1” onto the console?
Well, the output of the above program will be
3
1
2
In order to understand this, we need to understand how Javascript maintains the order of functions internally.
Call Stack
Javascript has something called a call stack, to keep track of the order of functions to be executed. The call stack, as the name suggests, is a stack. Hence, items added to this stack will exit out of the stack in a ‘last in, first out’ order.
In the above program, when the program reaches line 14, the Javascript program sees that the function sayOne is to be called. When this happens, it adds function SayOne to the call stack. So, the stack currently looks like this
| |
| |
| |
| |
| sayOne() |
|____________|
Call stack
This function remains in the call stack and is popped out only after its execution is completed. The way Javascript works, it always first executes the function at the top of the stack, then pops it out of the stack and then moves to the next function in the stack. Hence, the program now ‘jumps’ into the execution of the function at the top of the stack, which is the sayOne function. The execution of sayOne starts at line 2, where the program sees that the sayThree function is to be called. So, Javascript adds this function too to the call stack. The updated call stack now looks like this
| |
| |
| |
| sayThree() |
| sayOne() |
|_____________|
Call stack
The program then jumps to the execution of the function at the top of the stack, which is the sayThree function. The code of sayThree function is now run, in which line 11 prints “3” onto the console. The program then sees that it has reached the end of the sayThree function and hence pops it out of the stack. So, the stack now looks like this
| |
| |
| |
| |
| sayOne() |
|_____________|
Call stack
Javascript then sees that the sayOne function is at the top of the stack and hence, jumps back to where it left off in its execution. Line 3 prints “1” onto the console. Once more, upon reaching the end of sayOne function, Javascript pops it out of the stack, which then looks like
| |
| |
| |
| |
| |
|_____________|
Call stack
After seeing that the stack is empty, the Javascript program then jumps back to where it left off in the original execution, which is to line 15, where it sees that sayTwo function is called. As you might have guessed, sayTwo gets added to the stack
| |
| |
| |
| |
| sayTwo() |
|_____________|
Call stack
Since it’s the topmost function in the stack, the program’s execution jumps to that of sayTwo, where in line 7, the value of “2” is printed on to the console. Upon reaching the end of the function, it is popped off the stack and the call stack is now empty. There are no more lines to run and hence the program terminates.
While we were discussing the call stack, you might have been a bit confused as to how Javascript ‘jumps’ during its execution to the function at the top of the call stack. How does Javascript know when a new function is added to the stack or when a function is removed from the stack and hence it needs to resume execution of the next function in the stack?
I shall be discussing that and much more in Part 2 of this series.
This post was originally published here on Medium.
Top comments (0)