Intellij has an inspection (i.e. lint check) that tells you functions that return Deferred
should be named something ending in Async
.
Naming conventions like these make sense to me in dynamically typed languages. But Kotlin has such a nice type-checker and ecosystem of tooling, so why rely on the convention?
Especially because Kotlin coroutines bake structured concurrency in, the function will probably also take a CoroutineScope
parameter, which would serve the same visual cue at the call site:
suspend fun doStuff() = coroutineScope {
doStuffAsync(this /* CoroutineScope */).await()
//...
}
As a side note, I understand the inspection's message that you'd rarely want a function that returns a Deferred
instead of a suspend
function. That's not my question. My question assumes that you know what you're doing and you want a Deferred
.
By convention, you append "Async" to the names of methods that have an Async or async modifier. Which doesn't even mention that your own asynchronous methods returning Task need the Async suffix, which I think we all agree they do. So the answer to this question could be: both.
Async methods can have the following return types: Task<TResult>, for an async method that returns a value. Task, for an async method that performs an operation but returns no value. void, for an event handler. Starting with C# 7.0, any type that has an accessible GetAwaiter method.
If the function that contains the asynchronous task returns a promise, the asynchronous operation will block just that function. Now, how do you handle async functions that return values?
What is the convention for suffixing method names with "Async". The Task-based Asynchronous Pattern (TAP) dictates that methods should always return a Task<T> (or Task) and be named with an Async suffix; this is separate from the use of async.
To begin with, a function should almost never return a Deferred
that comes from an async
block. It is up to the caller to wrap some unit of work into an async
while doing other work in the foreground, then await
on the async result before returning, and wrap all that code in a coroutineScope
.
The intended source of Deferred
instances is the adaptation layer between Kotlin coroutines and 3rd-party async APIs. For such calls it does make sense to put Async
in the name and even some Java APIs follow this convention. For example, you may have a
fun fetchOrder(id: String): Deferred<Order>
and use it as
val orderCancelled = fetchOrder(orderId).isCancelled
This code is type-safe and type-correct, it doesn't cause any compiler errors. It looks like it's fetching an order from a remote system and then checking the order status (whether it's cancelled), but what it's actually doing is getting a Deferred<Order>
and checking whether the Deferred
is cancelled. Because your function name is missing Async
, this kind of error is hard to spot.
Yes, you can also request the IDE to give you the return type, but it may take a while before you even suspect what's going on.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With