Asynchronous Programming with Kotlin- 3 mins
Kotlin coroutines are a way of doing “asynchronous or non-blocking programming”, but what does it mean to be “asynchronous” and “non-blocking”?
Asynchrony, Concurrency, and Parrallelism
To understand Asynchrony, let’s define it along with other terms used in the same context: Concurrency and Parallelism:
A simple definition of asynchrony is the following:
“Asynchrony, in computer programming, refers to the occurrence of events independent of the main program flow and ways to deal with such events”
Asynchrony is a programming model where a program starts some tasks, without waiting/blocking for the results of the tasks. The program continues his work until receiving a signal that the results are available.
In programming, concurrency is the composition of independently executing processes (…), [and] dealing with lots of things at once (…).
— Rob Pike
Concurrency is about composition: handling multiple tasks being in progress in the same time, but not necessarily simultaneously or with a specific order.
Parallel computing is a type of computation in which many calculations or the execution of processes are carried out simultaneously
Parallelism, often mistakenly used for concurrency, is about simultaneous execution of multiple tasks.
In programming, (…) parallelism is the simultaneous execution of (possibly related) computations. , [and] doing lots of things at once (…).
— Rob Pike
Now that we have defined asynchrony, what is the problem we are trying to solve ? Let’s consider a simple web-application that:
- Receives request from a client.
- Reads a local file.
- Uploads the file to some server.
- Returns back to the client the URL of the uploaded file.
The picture below shows the application might work in the single-thread mode:
This only works for a single request, when the working thread is busy handling a request, it won’t be able to respond to another request in the same time!
Thread by request
A solution might be to have a thread for each request! however, thread creation is expensive and this approach has a limit: the number of thread OS can manage concurrently!
What about using thread pools? A limit here is once all the threads are busy, each new request will have to wait until a thread is available.
Yield and Continue
If a thread is doing nothing but waiting for an I/O operation (file or network), why not just to re-use it? for this to work, we need each request to yield the thread to another request instead of keeping the thread and waiting, then continue later when the waiting is done.
Each function should split into chunks, release the thread after running a chunk, and continue the next chunk once the result it needs is ready. The functions are cooperating to use thread effectively and thus approach is called cooperative or non-preemptive multitasking. This is exactly what coroutines are about!
For further understanding, please read this excellent article and check the sources section below.
Reactive Streams and Coroutines
Reactive streams and Kotlin coroutines are often being compared. The following citation is an excellent answer to this comparison:
(…) RxKotlin does not use coroutines yet; the reason is quite simple–both coroutines and Schedulers in RxKotlin share nearly the same internal architecture.
— Reactive Programming in Kotlin
In other terms, coroutines and Rx are simply two different layers of abstraction. However, in the near future, Kotlin Flows could be a solution to this dilemma.