If you are only getting started with programming, chances are, you are thinking about programs as a set of sequential blocks of logic, where each block does a specific thing and passes its result so the next block can run and so on, and for the most part you are right, most programs run in a sequential manner, this model allows us to build programs that are simple to write and maintain. There are specific use cases however where this sequential model wouldn’t work, or wouldn’t be optimal. As an example, consider a book reader application. This application has a few advanced features such as finding all occurrences of a word, navigating between bookmarks, and similar. Now imagine the user is currently reading a long book and decides to look for all the occurrences of a common word such as “The”. The application will normally take a couple of seconds to find and index all the occurrences of that word. In a sequential program, the user cannot interact with the application (change the page or highlight a text) until the search operation is fulfilled. Hopefully, you can see that that’s not an optimal user experience!
The diagram illustrates a typical execution flow of the book reader application. If the user initiates a long-running operation (in this case the search for all occurrences of “the” in a large book), the application “freezes” for all the duration of that operation. In this case, the user will keep clicking on the next bookmark button with no result until the search operation is finished and all the operations will take effect at once giving the end user the feel of a lagging application.
You might have noticed that this example doesn’t really correspond to the sequential model we introduced earlier. This is because the operations here are independent of each other. The user doesn’t need to know about the number of occurrences of “the” in order to navigate to the next bookmark, so the order of execution of operations is not really important. We don’t have to wait for the end of the search operation before we can navigate to the next bookmark. A possible improvement to the previous execution flow is based on this logic: we can run the long search operation in the background, proceed with any incoming operations, and once the long operation is done, we can simply notify the user. The execution flow becomes as follows:
With this execution flow, the user experience is significantly improved. Now the user can initiate a long-running operation, proceed to use the application normally, and get notified once the operation is done. This is the basis of asynchronous programming.
Javascript, amongst other languages, supports this style of asynchronous programming by providing extensive APIs to achieve just about any asynchronous behavior you can think of. At the end of the day, Javascript should be inherently an asynchronous language. If we refer to the previous example, the asynchronous logic is at the base of all user-interaction applications, and Javascript was primarily built to be used on the browser where most of the programs are about responding to user actions.
There are many ways to go about implementing Asynchronous code with Javascript, it all depends on the context and use case you are going for, typically, there are 3 approaches that are the most commonly used:
- Callbacks: callbacks are the traditional way of implementing asynchronous functions in Javascript. The premise is simple: an asynchronous function has an extra parameter which holds a callback function, the callback function will be executed as a response to the completion of the asynchronous function.
- Promises: Promises are built on top of callbacks and are built to expand on their logic. Rather than specifying a single callback function, a promise has a resolve function which runs on a successful completion of the asynchronous function, and a reject function that runs in case of an erroneous completion.
- Async/Await: the purpose of the Async/Await pattern is to bring the simplicity of synchronous programming to the asynchronous world. Async/Await serves a similar utility to Callbacks and Promises but abstracts their complexities with an intuitive syntax.
For a more comprehensive explanation that goes through the presented example from the lens of these different patterns, refer to the full article on the Code Labs Academy blog.
Top comments (0)