In this blog, we will be talking about how this synchronous programming language, performs asynchronous non-blocking code.
Most code is synchronous, where it’s executed line by line; each line of code waits for the previous line to finish. Long-running operations block code execution, for example, downloading a large file with a poor connection.
ES2015 → Callbacks to Promises
ES2017 → Implementation of Async and await
The problem: Callbacks do not work well at scale as the complexity of the tasks grow.
This usually results in nesting callback functions otherwise known as callback hell, deep nesting goes against common coding practices.
The solution: Promises ✅
This deep nesting led to the need for promises. Promises are a container for an asynchronously delivered value/container for a future value e.g., a response from an AJAX call. We don’t get a response initially, but we know we’ll get one in the future. Once we get a response these promises are used/consumed or handled for errors.
“Pending” implies the operation is neither fulfilled nor rejected, “Fulfilled” indicates the operation was successful and “Rejected” implies that the operation failed.
Promises allow us to no longer rely on events and callbacks passed into asynchronous functions to handle asynchronous results.
Instead of nesting callbacks, we can chain promises for a sequence of asynchronous operations: hence escaping callback hell. This solution however does not come without its own challenges. Chaining promises introduces syntax complexity.
The solution: The async function and the await Keyword ✅
Async and await are built on promises. The purpose of async/await is to reduce the complexity of the syntax e.g., reducing the boilerplate around promises, and the “don’t break the chain” limitation of chaining promises.
Through async and await we can make the code look like it’s synchronous, but it’s asynchronous and non-blocking behind the scenes.
A quick overview of how asynchronous code is executed behind the scenes in the browser:
The two most important aspects are the call stack Queue and the event loop:
Asynchronous Functions are handled by the Micro-tasks Queue (Similar to Call stack). This queue specifically handles promises. It’s important to realise it has priority over the call stack queue.
Then Event Loop keeps running continuously and checks the Main stack, if it has any frames to execute, if not then it checks the Callback queue, if Callback queue has codes to execute then it pops the message from it to the Main Stack for the execution.
Callback Queue: This is where your asynchronous code gets pushed to, and waits for the execution.
Heap: This is where all the memory allocation happens for the variables, that you have defined in your program.
Web API: Handles AJAX and JSON requests.
‘Kash works as a full-stack software developer at WayMaker Digital, helping clients to solve business and technology challenges.
WayMaker Digital provides technology consulting, product design and information products, that enable our clients to consistently thrive and innovate to meet user needs. The quality of our experiences and ideas set us apart when it comes to service delivery, customer experience design and product development.
Our team of consultants have built digital experiences for leading brands with complex business scenarios ranging from startup companies to public services and from telecoms to e-commerce; no project deliverable is too complex.
Our approach: we adopt human-centred & design thinking models to solve complex business challenges, which ensure our clients are on top of their business.
Do you have a question or an enquiry; you want to discuss a project or need a free consultation? Give us a shout here.