I know, I'm late to the party, but I finally dug into how JavaScript handles async/await
under the hood—so here I am.
Everyone knows what async/await
does, but here's a quick summary anyway:
async/await
is a way to handle async ops in JavaScript. It makes your code look synchronous, saves you from the "callback hell" while handling Promises in the background. Coming from a mobile app background, I'm familiar with how Kotlin Coroutines work and how suspend functions operate internally.
On the surface, they seem pretty similar code-wise:
Kotlin Coroutines
suspend fun fetchData() {
val data = withContext(Dispatchers.IO) {
// do stuff here
}
}
JavaScript
async function fetchData() {
const data = await fetchFromApi();
}
In both cases, the function pauses until the async op. is complete, keeping the code simple and readable.
JavaScript Promises
A Promise is an object that represents the eventual completion (or failure) of an asynchronous operation and its resulting value.
But how does async/await work with these Promises behind the scenes?
When you mark a function as async
and use the await
keyword inside, here's what happens:
Checking for Promises:
When JS encounters theawait
keyword, it first checks whether the value you're awaiting is a promise. If it's not, the language engine immediately wraps it into an immediately resolved promise.Non-Blocking Execution:
If the value is indeed a promise, such as when you useawait
with thefetch
method, JavaScript doesn't block the main thread for other methods in call stack. Instead, it runs the promise in the background.Call Stack Management:
JavaScript'sevent loop
andmicrotask queue
come into play. When theawait
keyword is found, the function is temporarily removed from the call stack, allowing other functions to execute.Resumption of Execution:
The function's execution pauses but doesn’t stop entirely. The promise runs in the background, and once it’s fulfilled, theevent loop
resumes the function from the line following theawait
keyword.