{"id":11084,"date":"2020-09-11T10:30:43","date_gmt":"2020-09-11T14:30:43","guid":{"rendered":"https:\/\/www.carnaghan.com\/?p=11084"},"modified":"2023-10-14T23:14:38","modified_gmt":"2023-10-15T03:14:38","slug":"understanding-asynchronous-javascript","status":"publish","type":"post","link":"https:\/\/www.carnaghan.com\/understanding-asynchronous-javascript\/","title":{"rendered":"Understanding Asynchronous JavaScript"},"content":{"rendered":"\n
The term “asynchronous” is often associated with JavaScript, which can be a confusing concept, especially for beginners. This can become even more confusing because JavaScript is a synchronous language with asynchronous capabilities. In order to understand asynchronous development in general, we need to start with the difference between synchronous and asynchronous programming. In simple terms, executing one task or item at a time is often referred to as synchronous, while executing multiple tasks (or threads) at a time is referred to as asynchronous.<\/p>\n\n\n\n
JavaScript is synchronous, single-threaded, and blocking language. It is synchronous, meaning it can execute one statement at a time. It is single-threaded, meaning, it has a single “call stack”<\/a>. We will discuss blocking later, but for now let’s take a look at how JavaScript executes code:<\/p>\n\n\n\n It is a very simple code, but we need to dig a little bit deeper and look at execution context and the call stack. An environment where the JavaScript code is evaluated and executed is called the Execution context. There are two types of execution context<\/a> in JavaScript, Global Execution Context (GEC), and Functional Execution Context (FEC). The execution contexts are stored in a stack called the call stack. There is only one call stack in JavaScript because it is a single-threaded programming language. The call stack is based on LIFO, meaning, Last In First Out. Let’s break down the above code to better understand the call stack.<\/p>\n\n\n\n 1. When the code is executed, a global execution context is created which is the first entry in the call stack.<\/p>\n\n\n\n 2. Next, console.log(“Executed first…”) is pushed into the stack.<\/p>\n\n\n\n 3. After its execution is completed, it is popped out of the stack.<\/p>\n\n\n\n 4. After that, fun() is pushed into the stack.<\/p>\n\n\n\n 5. Now, fun() is executed. console.log(“Executed in the function”) is pushed in the stack.<\/p>\n\n\n\n 6. After the console statement is executed, it is popped out.<\/p>\n\n\n\n 7. Function execution ends and fun() is popped out.<\/p>\n\n\n\n 8. Next, console.log(“Executed last…”) is pushed into the stack.<\/p>\n\n\n\n 9. console.log(“Executed last…”) executes and is popped out.<\/p>\n\n\n\n 10. Program execution is completed, so the main() is also removed from the stack.<\/p>\n\n\n\n The purpose of the above demonstration was to illustrate how JavaScript executes code line by line and only moves to the next line when the previous command has completed its execution. This is synchronous JavaScript. I also wrote an earlier article which explains the call stack in context of synchronous execution in more detail see: JavaScript Call Stack – Ian Carnaghan<\/a>.<\/p>\n\n\n\n In the example, nothing was blocked, but there can be scenarios when the call stack is blocked due to reasons such as calling APIs, which can block the call stack. Network requests generally take time, therefore blocking the call stack:<\/p>\n\n\n\n In the above code, the “APIcall” function is calling an API. First, the APICall function completes execution followed by, console.log(“Execution ends here…”). Since the APICall function is making an API call, it can take some time to complete. Therefore, the execution is blocked at this point and the console statement will not run until APICall is completed. This illustrates the downside of synchronous programming. In asynchronous programming, execution would not stop. It would not wait for the APICall function to complete the execution, instead it would move further, executing the console statement. One way to achieve this is through the use of callback functions.<\/p>\n\n\n\n JavaScript functions are considered first-class functions<\/a>. A JavaScript function can be assigned to a variable, passed to another function as a parameter, and also it can be returned from another function. You can learn more about this here<\/a>.<\/p>\n\n\n\n A function passed to another function as an argument is known as a callback function. Here’s a simple example:<\/p>\n\n\n\n Normally, console.log(“Executed first…”) should be executed first, followed by the setTimeout function, and finally console.log(“Executed last…”). But the above code is not synchronous, it is asynchronous. To understand this better, take a closer look at the setTimeout function.<\/p>\n\n\n\n The first parameter of the setTimeout function is a callback function and the second parameter is 5000, meaning 5 seconds. This means, the callback function will be executed after 5 seconds. The output of the code should look like this:<\/p>\n\n\n\n Notice the execution will not wait for 5 seconds, instead it will move to the next statement and execute the console command – console.log(“Executed last…”). After 5 seconds, console.log(“Executed after 5 seconds…”) will be executed. This is not synchronous nor blocking, instead it is asynchronous and non-blocking.<\/p>\n\n\n\n The setTimeout function waits for 5 seconds while the execution moves to the next line. After 5 seconds, the callback function of the setTimeout function is executed. This is how the callback function prevents blocking and helps in asynchronous programming.<\/p>\n\n\n\n Going back to the call stack again, here’s what is happening:<\/p>\n\n\n\n 1. console.log(“Executed first…”)<\/strong> is pushed into the stack.<\/p>\n\n\n\n 2. After its execution, the console statement is popped out and the setTimeout function is pushed into the stack.<\/p>\n\n\n\n The setTimeout function executes. It will wait for 5 seconds and then execute the callback function. This function is a part of the browser’s web APIs. The timer is pushed in the web API environment.<\/p>\n\n\n\n 3. In parallel, the execution of setTimeout has ended and it is popped out of the stack while the next statement – console.log(“Executed last…”) is pushed into the stack.<\/p>\n\n\n\n 4. After the console statement has executed, it is popped out of the stack.<\/p>\n\n\n\n At this point, the output is:<\/p>\n\n\n\n Meanwhile, the timer is still going on in the web API environment and when it ends, the callback function is pushed into the “message queue”.<\/p>\n\n\n\n 5. At the point when the timer expires, the “event loop” checks the message queue if any callback exists. In our case, one callback is present in the message queue, and is pushed into the call stack.<\/p>\n\n\n\n 6. The callback executes.<\/p>\n\n\n\n 7. After the console statement is executed, it is popped out along with the callback function.<\/p>\n\n\n\n 8. In the end, the execution is completed and we have the following output.<\/p>\n\n\n\n Note two important terms here – message queue and event loop.<\/p>\n\n\n\n Next, let’s take a look at creating our own callback function:<\/p>\n\n\n\n The “add” function has three arguments – x, y, and a callback function. The sum of x and y is calculated in the add function and then, it is passed to the callback function. We can also break out the above code this way:<\/p>\n\n\n\n Instead of passing an anonymous function, here we just create a separate function.<\/p>\n\n\n\nfunction fun(){\n console.log(\"Executed in the function\");\n}\n\nconsole.log(\"Executed first...\");\n\nfun();\n\nconsole.log(\"Executed last...\");\n<\/code><\/pre>\n\n\n\n
main() -><\/code><\/pre>\n\n\n\n
main() -> console.log(\"Executed first...\")<\/code><\/pre>\n\n\n\n
main() -><\/code><\/pre>\n\n\n\n
main() -> fun() -><\/code><\/pre>\n\n\n\n
main() -> fun() -> console.log(\"Executed in the function\") -><\/code><\/pre>\n\n\n\n
main() -> fun() -><\/code><\/pre>\n\n\n\n
main() -><\/code><\/pre>\n\n\n\n
main() -> console.log(\"Executed last...\") -><\/code><\/pre>\n\n\n\n
main() <\/code><\/pre>\n\n\n\n
function APICall(url){\n \/\/ some operation\n}\n\nAPICall(url)\n\nconsole.log(\"Execution ends here...\");\n<\/code><\/pre>\n\n\n\n
Callback functions<\/h2>\n\n\n\n
console.log(\"Executed first...\");\n\nsetTimeout(() => {\n console.log(\"Executed after 5 seconds...\");\n }, 5000);\n\nconsole.log(\"Executed last...\");\n<\/code><\/pre>\n\n\n\n
setTimeout(() => {\n console.log(\"Executed after 5 seconds...\");\n}, 5000);\n<\/code><\/pre>\n\n\n\n
\n
main() ->; console.log(\"Executed first...\")<\/code><\/pre>\n\n\n\n
main() -> setTimeout()<\/code><\/pre>\n\n\n\n
main() -> console.log(\"Executed last...\")<\/code><\/pre>\n\n\n\n
\n
main() -> callback()<\/code><\/pre>\n\n\n\n
main() -> callback() -> console.log(\"Executed after 5 seconds...\")<\/code><\/pre>\n\n\n\n
main()<\/code><\/pre>\n\n\n\n
\n
\n
function add(x, y, callback){\n var z = x + y;\n callback(z);\n}\n\nadd(10, 20, function(z){\n console.log(\"Double of z:\", z * 2)\n})<\/code><\/pre>\n\n\n\n
function double(z){\n console.log(\"Double of z:\", z * 2)\n}\n\nfunction add(x, y, callback){\n\tvar z = x + y;\n\tcallback(z);\n\n}\n\nadd(10, 20, double);<\/code><\/pre>\n\n\n\n
Callback hell<\/h2>\n\n\n\n