Kotlin’s suspend functions compared to JavaScript’s async/await
With the release of Kotlin 1.3, coroutines went out of experimental status, so I felt like writing about them to help people understand what they are and how they work, and also because explaining is the best way to learn something.
When writing examples, I realized that Kotlin’s suspend
functions felt like JavaScript's async
functions to JavaScript developers, so I decided to write about the differences and similarities in this separate story in order to avoid confusions.
TL;DR
Let’s assume myAsyncFunc
is a JavaScript async
function, and mySuspendFunc
is a Kotlin suspend
function, both declared with the same body. We can draw the following parallel:
- JavaScript
myAsyncFunc()
is similar to Kotlinasync { mySuspendFunc() }
- Kotlin
mySuspendFunc()
is similar to JavaScriptawait myAsyncFunc()
In short, Kotlin explicitly declares at the call site that the call is asynchronous via the async()
function call. On the other hand, if something looks like a normal function call, it is implicitly synchronous and we can expect a result directly.
In JavaScript, normal calls of an async
function are implicitly asynchronous, because they return a Promise
. JavaScript is explicit about making these calls synchronous via await
.
We want to see code!
Let’s compare these 2 snippets. Note that I aligned the line numbers for easier comparison.
We should note that these are not exactly equivalent at every function level, but the call chain of normalFunction()
as a whole is roughly equivalent.
The similarities
To come back to the 2 points I made in the TL;DR, let’s look at lines 19 and 11.
They both can do async stuff
Line 19 represents an asynchronous call in both snippets. Calling an async
function directly (without await
) in JavaScript returns immediately and returns a Promise
.
This is similar to wrapping a Kotlin suspending function call into an async()
library function call, which returns immediately too (so it’s asynchronous), and returns a Deferred<T>
.
They both replace the callback hell
Line 11 represents a synchronous call in both snippets. Calling a suspend
function in Kotlin is similar to using await
+ an async function
call in JavaScript.
Behind the scenes, suspending functions are technically an abstraction over callbacks* to allow you to write sequential code, just like await
kind of unfolds Promise
callbacks. In order to do this “callback unfolding magic”, calling a suspending function requires the caller function to be declared suspend
, just like using await
in JavaScript forces you to declare your function with async
.
*To be taken with a grain of salt. Neither Kotlin nor JavaScript (AFAIK) actually de-sugars this code into callbacks. They internally use state machines instead, for better efficiency. However, it gives the same apparent behavior from the outside.
The differences
From afar: they have different defaults
JavaScript and Kotlin have different opinions as to what should be the default behavior and what needs to be explicitly stated in the code.
Kotlin suspending functions are synchronous by default, which means if you call them without special syntax, you can expect what a normal function call would do: wait for execution of the function and return the resulting value. They can be wrapped in coroutine builders like async()
to get an asynchronous behavior (this creates a new coroutine to execute the suspending function passed as parameter).
JavaScript async
functions are asynchronous by default. They return a Promise
and return immediately when called without special syntax. They have to be explicitly await
-ed if we want to call them synchronously and wait for their result.
A closer look: they don’t do the same things in the same place
What appears to be syntactic differences is in fact slightly more. The async
keyword in JavaScript does multiple things:
- it allows the “callback unfolding magic” of
await
, likesuspend
in Kotlin allows calls to other suspending functions - it also makes the function asynchronous and wraps the return value (or error) into a
Promise
.
Kotlin sees that second point as a separate thing, and requires you to do something else to express asynchronous calls explicitly (starting a coroutine).
I feel like drawing parallels like this can help people grasp faster how both suspending functions and JavaScript async/await works, and I hope this helped you. If you find something misleading or incorrect in what I said here, please don’t hesitate to comment. Cheers!