Many programs rely on some kind of external action or interaction to run. For example, a website may have to fetch data from an API or from a database. A video game might have to read game files from a disk. A DAW might have to wait for you to press the spacebar to start recording your voice etc.
Those events can be very slow (like a disk read) or can potentially even never happen (you can chose not to press the spacebar). To make these programs work, your initial thought might be to simply wait for them to complete. However, if we wait by just having our CPU idle until a condition is true, our poor CPU is doing nothing while our slow, even potentially interminable operation completes.
This method of just continuously waiting until a certain condition is true is called busy waiting, which is a form of polling. In code we can imagine it as something like this:
while (!condition) {
//wait and do nothing...
}This is generally considered a very poor solution since we completely block our machine from doing anything else while we wait for the thing.
Therefore, we come up with a better solution, where we let the CPU continue operating while that external operation does its thing. This is called asynchrony.
Issues with Async Programming
If something has to happen after an event, then it must be triggered by the event. It must be part of or called by the event’s handler.
The problem here is that we usually request data from I/O and need it to proceed. Most of the executable code depends on that returned data. Sometimes a sequence of requests must be made that depend on each other as well.
This leads to a special form of functions called callbacks.