Async/Await Tips To Be Aware of at All Times | by jsmanifest | Aug, 2022

Ensure that you await for these useful tips too

1*qf XWVMC9dQ 0DNJRol78Q
Photo by

Asynchronous programming in JavaScript has long been a challenge for us developers. The problem is that it’s difficult to write asynchronous code.

Before Promises made it into the scene, the callback pattern was the most efficient way to write asynchronous code. However, this pattern still suffered from unmaintainable code like callback hell, especially in code that heavily depends on asynchronous operations.

Eventually, Promises were introduced to solve the issues developers often encountered from using callbacks to write asynchronous code. Even with Promises, however, there was still confusion that developers faced.

Promises need to implement callbacks that return a new or existing promise that must implement another callback that returns the next value (which can be another Promise, and so on). This can eventually bring up previous issues again, such as callback hell.

The way we are taught to express our code (and writing in general) is a little different than the way JavaScript code is executed during the runtime. There is nothing wrong with JavaScript or even other programming languages in design, nor is it an issue of abstraction. Reading text from top to bottom and left to right is human nature.

JavaScript needed a proper way for developers to write asynchronous code of this nature and eventually introduced a solution with the ECMAScript standard of async functions and the await keyword as an expression. This is, in short, commonly referred to as async/await.

In this post, we will be going over some important tips that every JavaScript developer must know to prevent difficult bugs from occurring in your applications.

One tricky gotcha when dealing with errors in async/await functions is returning promises that reject the caller of the current function. When doing this, we’re forwarding our rejection handling to the caller.

Let’s take a look at the code example below:

Inside our start function, we return the result of the function getRandomItem and expect our rejections to be caught in its local try/catch block because we want our JavaScript runtime to be able to continue running by just displaying an alert window to the user. However, when we run the code, it actually doesn’t display a popup at all, and instead, the JavaScript runtime crashes after executing our Throw we put in our getRandomItem function:

0*E1g6L6wDPPo Gq2d

This is because our Promise is being returned by getRandomItem is not being awaited in the same function block:

To fix this, all we need to do is add a simple await keyword so that it becomes awaited in the same function block:

It’s important to understand that this only occurs when returning an entirely new promise, so returning regular values like numbers aren’t affected:

Newer JavaScript developers are more susceptive to this “gotcha” when writing asynchronous code.

Here is an example:

Logging this to the console gives us an empty array[] which might not have been what we expected. forEach functions ignore promises that are returned on each loop!

In other words, this is technically the same as:

Iteration functions like the map can work when collecting and resolving promises because they return the result of invoking the callback on each loop. In JavaScript, any function that returns a promise becomes a thenable (an object with a .then method).

Promises are also thenables, and since map returns each result of our callback this means we can use an asynchronous version of a callback function rather than the synchronous version if needed.

This is technically the same as the following:

In the previous example, we can make use of Promise.all on the final result that map returns (Which can ultimately become a collection of thenables or promises if we returned them from our callback handler, if you remember):

Here’s the result:

Promises can eventually crash our app if they become stuck in a never-ending recursion cycle. And no, I’m not talking about these types of recursions:

I’m talking about promises that get caught in a neverending recursive promise chain that never actually resolves from invoking itself.

For example, in this code snippet below, we have an asynchronous function that returns a thenable which resolves and returns the invocation of the original promise call (start). This returns another thenable (delay), which returns another invocation of the original promise call, and so on:

The big issue with this is that the original call to the start function never actually has a way to find a resolution to the original promise call. This causes a memory leak in our programs that eventually causes them to crash.

So, how can we avoid this?

We can leverage the concept similarly to forEach we discussed earlier, and instead of returning the promise, the result we can make the function ignore the promise result (just like what forEach does). So, every invocation reaches the end of their function block after running through them:

Although this solves the memory leak, it completely removes the ability to capture inner promise rejections from new invocations.

We can make start catch any of its deeply nested rejections with a simple wrapper which solves our issue. Here’s the code:

I hope you found this valuable and look out for more in the future!

News Credit

%d bloggers like this: