⏳ Creating a Reusable Loading Screen Manager for Async Tasks in Typescript

Jeremy C. Zacharia
3 min readMar 4, 2020
Source: Unsplash

It is often the case that the user of an application must wait while a request to a back-end is being processed. Usually, a loader is shown during that time to tell the user that stuff is happening. That’s good UX. When implementing a loader, I found myself repeating code that: shows a loader, awaits task completion, then closes the loader. So why not create a helper function that enhances this common pattern?

In this example, there is a loading controller service that shows a popup of a loader.

Load Task

Since this is an async function, each line with the await keyword will await its completion before continuing the execution of the function.

This code does 4 things:

  • creates a loader and awaits its instantiation
  • awaits the loader’s presentation for the user to see
  • awaits the request to the backend to be completed
  • awaits the dismissal of the loader

However, there are some problems with this approach:

  • we have to repeat this code for each back-end request the user makes
  • does not handle success nor error response
  • not generic nor robust
  • boring

So let’s create a generic solution that we can use throughout our application with the example of a user login system! 🔑

1️⃣ Let’s say we have a login form with this function for signing in our user.

Simple Login Function with Load Task

But this approach is not reusable. What if we wanted to reuse this load task for when a user creates an account?

So let’s create a reusable function for our load task.

Reusable Load Task Manager

Upon invoking the provisionLoadTask function, the task callback is the function that is invoked after presenting the loader. Once task is resolved, the loader is dismissed.

Note that we can wait for multiple tasks to complete in parallel by calling Promise.all :

provisionLoadTask(() => Promise.all([task1(), task2()]));

Or in order using an async function:

provisionLoadTask(async () => {
const res = await task1();
return await task2(res);
});

We are now able to reuse this load task logic but there are some obvious problems with this solution:

  • If the email and password are correct, how do we handle the resolution response?
  • If the email and password are not correct, how do we handle the rejection error?

2️⃣ To address this, let’s add callbacks for success handling and error handling.

This is quite an improvement!

With this approach, notice how we can handle a login success and login failure with independent callbacks! Note these two callbacks will never both be executed.

It is important to note the use of isSuccessful .

If await task() is successful, isSuccessful is set to true and the success callback is called.

If await task() is unsuccessful, isSuccessful is set to false and then the error callback is called.

This solution is missing a key feature though.

3️⃣ Let’s say that upon login success you want to route the user to a secured portal page like so:

If you run this code, you’ll notice that when you submit the login form, the loader will popup, do authentication stuff, dismiss, then you will route to the portal page. This is because the dismissal of the loader comes before the success and error callbacks. We want to dismiss the loader when we have completed all the tasks, including routing, or else it’s bad UX.

We can make an improvement by calling dismiss after the success and error callbacks and add await in front of callbacks so that the loader will not be dismissed until after all the awaited tasks in either callback are resolved and the callback returns! 🤓

And of course, there might be some code to execute on success or failure so let’s add a complete callback also.

If you run this code, the loader will be dismissed after routing to the portal page.

There you have it! A generic solution for provisioning a loader while executing async tasks with proper error handling!

--

--