Single-Threaded Asynchronous Programming
Introduction
Many programming languages, such as JavaScript and Python, support single-threaded asynchronous programming natively. Previously, I have discussed the mechanism of Python asynchronous programming using asyncio
and some of its key data structures, such as event loop and awaitables.
Because there are so many details in those blog posts, the reader might not be able to capture the key ideas from a high-level perspective. In this blog post, I would like to discuss the key ideas of single-threaded asynchronous programming that are programming language agnostic.
How Single-Threaded Asynchronous Works?
Key Data Structures and Algorithms
The key data structures for asynchronous programming are:
- Event Loop
- Schedules Queue
- Callback Queue
The event loop runs when there is no calls in the call stack of the program. When the event loop runs, it checks the schedules queue and the move the schedule that has the highest priority and is ready to run to the callback queue, which is sometimes called as ready queue. The callback corresponding to the schedule that is ready in the callback queue will then be placed to the call stack of the program for execution.
Example
Let’s consider a scenario where we want to sleep for 5 seconds.
If the sleep call was directly placed on the call stack of the program, then it will be a blocking call for 5 seconds. During this period of time, the program could not do other things but wait.
However, if the sleep was placed on the schedules queue, when the call stack is empty, the event loop will check if the sleep schedule in the schedule queue is ready (by comparing the absolute time difference between the time that the sleep schedule was placed onto the schedules queue and the time the event loop is checking to see if it is greater or equal to 5 seconds). Once the sleep schedule is ready, the sleep schedule will be placed on the callback queue, and its corresponding callback, which is usually simply assigning and returning a future variable, will be further placed on the call stack and executed. Assigning and returning a future variable is extremely fast and non-blocking. During the period of the time where the sleep schedule was not ready, new other schedules could be placed to the schedule queue, existing other schedules in the schedule queue could become ready, placed to the callback queue, and further the call stack of the program. We could see that, during waiting the sleep to be complete, the program is not blocked and able to do other things that are scheduled as well.
Conclusion
Single-threaded asynchronous programming is not hard to understand from a high-level. What’s difficult is how to implement to handle sophisticated scenarios in practice. Fortunately, usually the programming languages have taken care of that for us.
Single-Threaded Asynchronous Programming
https://leimao.github.io/blog/Single-Threaded-Asynchronous-Programming/