Aysnc in single thread
If Javascript Is Single Threaded, How Is It Asynchronous?
Javascript is a single threaded language. This means it has one call stack and one memory heap. As expected, it executes code in order and must finish executing a piece code before moving onto the next. It's synchronous, but at times that can be harmful. For example, if a function takes awhile to execute or has to wait on something, it freezes everything up in the meanwhile.
A good example of this happening is the window alert function. alert("Hello World")
. You can't interact with the webpage at all until you hit OK and dismiss the alert. You're stuck.
Asynchronous functions will run and execute only after all the synchronous functions have been executed. Until then, they will be processed in the background(C++ APIs).
It can't run inside a callback stack until it gets emptied. That means that after main()
is removed from call stack, only then can all asynchronous functions start executing.
So how do we get asynchronous code with Javascript then?
Well, we can thank the Javascript engine (V8, Spidermonkey, Deno, etc...) for that, which has Web API that handle these tasks in the background. The call stack recognizes functions of the Web API and hands them off to be handled by the browser. Once those tasks are finished by the browser, they return and are pushed onto the stack as a callback.
Open your console and type window
then press enter. You'll see most everything the Web API has to offer. This includes things like ajax calls, event listeners, the fetch API, and setTimeout. Javascript uses low level programming languages like C++ to perform these behind the scenes.
What is a Web API, in the context of the browser?
The browser gives us features that the JavaScript engine itself doesn’t provide: a Web API. This includes the DOM API, setTimeout, HTTP requests, and so on. This can help us create some async, non-blocking behavior.
They are also referred to as BOM (Browser Object Model) APIs. For example, the DOM API is a subset of the BOM APIs. Another example would be the Event
interface or the Element
interface, which both are part of the DOM API and consequently are also part of the BOM APIs.
Node.js is a JavaScript runtime environment that allows you to parse, compile, and execute your JavaScript code. Node does this with the help of the V8 engine, Google’s open source JavaScript engine written in C++.
With the V8 engine, Node has the capability to execute both JavaScript and C++ under the hood hidden from the user. This allows you to write both synchronous and asynchronous JavaScript code in a single-threaded environment without having to worry about threading or concurrency.
Source: Single-Threaded and Asynchronous — How Does Node Do It?
Example
const foo = () => console.log("First");
const bar = () => setTimeout(() => console.log("Second"), 500);
const baz = () => console.log("Third");
bar();
foo();
baz();
# First
# Third
# undefined
# Second
Source: JavaScript Visualized: Event Loop
- We invoke
bar
.bar
returns asetTimeout
function. - The callback we passed to
setTimeout
gets added to the Web API, thesetTimeout
function andbar
get popped off the callstack. - The timer runs, in the meantime
foo
gets invoked and logsFirst
.foo
returns (undefined),baz
gets invoked, and the callback gets added to the queue.- When the Javascript engine's event loop kicks in. It starts firing, waiting for events to be pushed into it.
- Since the
setTimeout
isn't finished, it returns undefined, as the default, well because it hasn't been given the value yet. Once the callback finally does hits we getconsole.log("second")
printed.
baz
logsThird
. The event loop sees the callstack is empty afterbaz
returned, after which the callback gets added to the call stack.- The callback logs
Second
.
Terminologies
-
Stack: A FIFO stack that keeps track of the next task to be executed on the main thread. If your statement is asynchronous:
setTimeout
,ajax()
,promise
, orclick
event, then that code gets forwarded to Event table, this table is responsible for moving your asynchronous code to callback/event queue after specified time. -
Heap: This is where all the memory allocation happens for your variables, that you have defined in your program.
-
Callback Queue (Queue in above diagram): This is where your asynchronous code gets pushed to, and waits for the execution.
-
Event Loop: Then comes the Event Loop, which keeps running continuously and checks the Main stack, if it has any frames to execute, if not then it checks Callback queue, if Callback queue has codes to execute then it pops the message from it to the Main Stack for the execution.
-
Job Queue (Not exsisted in above diagram): It is like a little kid asking "Are we there yet?" on a road trip.
Apart from Callback Queue, browsers have introduced one more queue which is “Job Queue”, reserved only for
new Promise()
functionality. So when you use promises in your code, you add.then()
method, which is a callback method. Thesethenable
methods are added to Job Queue once the promise has returned/resolved, and then gets executed.