Connected dots and lines

How to return response from an asynchronous call in JavaScript or TypeScript

As you probably know, JavaScript is a single-threaded programming language. It means that only a single block of code can be executed at a time.

However, the language allows you to create an asynchronous code. That can help you to create an efficient code in various specific situations. For example, when the data is taken from the database or network.

This tutorial will show you how to return from an asynchronous call. There are several methods that can be used in the code. As a result, please review the following section to learn more about the JavaScript language.

What is a synchronous call

That is the most common way of programming. The synchronous JavaScript code runs step by step. Each operation is executed only after the previous operation has ended.

If you call some function, the application enters into that function and executes the inner code. The outer code is executed only after the function returns back to the caller method.

Below, you can see a basic example of a synchronous code. We've created the sumOfArrayValues() method that calculates a sum of all elements in an array. That method is called with a single parameter. And the result is stored into the sumOfValues variable.

function sumOfArrayValues(values) {
    let result = 0;

    for (let i = 0; i < values.length; ++i) {
        result += values[i];
    }

    return result;
}

const sampleValues = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

let sumOfValues = sumOfArrayValues(sampleValues);

console.log(sumOfValues);

As you may see, the given application prints the result to the console. The console.log() method is executed only after the sumOfArrayValues() method finishes. This means that the code is synchronous. The console output is shown below.

developer@developer-pc:~/samples/javascript$ node index.js
55

What is an asynchronous call

Asynchronous programming is very important in web development. It allows you to make your application responsive while some other code is running. It can be used in various situations, like processing events, reading data from a network, etc.

The setTimeout() method is a good example. It executes the given callback with a specified delay. However, the setTimeout() method does not block the main thread. The execution continues after that method.

Below you can see our code sample. The "Timer has been initialized." notice is printed immediately after the timer is initialized. It appears first on the screen. Then, after one second, another message is printed to the console with help of the callback.

setTimeout(() => {
    console.log("This is a sample message.");
}, 1000);

console.log('Timer has been initialized.');

If you run the given code, the console output should be like it is shown below.

developer@developer-pc:~/samples/javascript$ node index.js
Timer has been initialized.
This is a sample message.

Using the async and await keywords

That's the modern way of making an asynchronous web application. The async and await keywords can be used to create a compact code that uses the JavaScript promises under the hood.

The Promises require you to add additional handlers to the code, like the then() and catch sections. However, this can increase the size of the code.

If you use those keywords, you can make a code that is visually synchronous. But internally, the application will pause its work and will wait for the completion if the await keyword is used in the code.

Also, the async keyword must be added to the function definition if there is the await keyword in the function body. That's something that you have to take care of when making the JavaScript code.

Finally, it is worth noting that the async function always returns a promise. That's why the await keyword is needed. It allows you to get the value that was returned by the function.

Please see the following code that shows how those keywords may be used in a real JavaScript application.

function delay(timeout) {
    console.log('Inside of the delay');
    return new Promise(resolve => setTimeout(resolve, timeout));
}

async function timeoutSample() {
    console.log('Before the delay');
    await delay(2000);
    console.log('After the delay');
}

console.time("duration");
await timeoutSample();
console.timeEnd("duration");

Now, let's try to run the given application. The output may be like it's shown below. The last message tells us that it took nearly 2 seconds to run the code.

developer@developer-pc:~/samples/javascript$ node index.js
Before the delay
Inside of the delay
After the delay
duration: 2.017s

Using the JavaScript promises

In very specific situations, you may directly use the Promises. That will give more flexibility to the code. You may have full control of how and when the promises are fulfilled. Also, you can reject the promise if something didn't work correctly.

Please see the following sample. It shows how to calculate the factorial of a number with the help of a promise. If the given number is not acceptable, we reject that value. Otherwise, the factorial is calculated and is passed into the resolve() method.

function calculateFactorial(number) {
    return new Promise((resolve, reject) => {
        if (number < 0) {
            reject("Number is negative.")
        } else if (number > 10) {
            reject("Number is too large.")
        } else if (number === 0) {
            resolve(1);
        } else {
            let factorial = 1;

            for (let i = 1; i <= number; i++) {
                factorial *= i;
            }

            resolve(factorial);
        }
    })
}

console.log('Starting the application.');

calculateFactorial(5)
    .then(factorial => {
        console.log('Factorial of a given number is: ' + factorial);
    })
    .catch(error => {
        console.error(error)
    });

console.log('The calculateFactorial() method has been called.');

If you run the given code, the output should be like it's shown below. You can see there that the factorial was successfully calculated for the number 5. The result is 120.

developer@developer-pc:~/samples/javascript$ node index.js
Starting the application.
The calculateFactorial() method has been called.
Factorial of a given number is: 120

Starting multiple promises and waiting for them to complete

Sometimes, you may want to create multiple promises in the application. It may be needed in various situations. Like, you need to process some data or fetch many files from the network.

However, separately waiting for each promise to complete is not an optimal solution. You'll have to write a lot of code to do it.

Instead, you may use the Promise.all() method. It allows you to wait until all the given promises are completed with a successful status. See how it should be done in the following code sample.

console.log('Starting the application.');

const integerPromise = Promise.resolve(3);

const floatPromise = Promise.resolve(57.234);

const stringPromise = Promise.resolve("hello");

const regularNumber = 777;

const timeoutPromise1 = new Promise((resolve, reject) => {
    setTimeout(resolve, 1000, 0);
});

const timeoutPromise2 = new Promise((resolve, reject) => {
    setTimeout(resolve, 1000, 0);
});

const promisesToWait = [
    integerPromise,
    floatPromise,
    stringPromise,
    regularNumber,
    timeoutPromise1,
    timeoutPromise2
]

Promise.all(promisesToWait)
    .then((values) => {
        console.log(values);
    })
    .catch(error => console.error('Error has occurred:', error));

console.log('The Promise.all() method has been called.');

The given sample should produce output like it's shown below. As you see, there is a list of values generated by our JavaScript promises.

developer@developer-pc:~/samples/javascript$ node index.js
Starting the application.
The Promise.all() method has been called.
[ 3, 57.234, 'hello', 777, 0, 0 ]

Now, let's see how the Promise.all() method works if a promise fails to run. This situation may occur in a real web application. It's important to create a code that handles such issues.

The catch() section will be triggered immediately if some promise calls the reject() method. However, the other promises may keep on running. The following code shows us such a behavior of a JavaScript application.

console.log('Starting the application.');

const integerPromise = Promise.resolve(3);

const floatPromise = Promise.resolve(57.234);

const stringPromise = Promise.reject("failure");

const regularNumber = 777;

const timeoutPromise1 = new Promise((resolve, reject) => {
    setTimeout(resolve, 1000, 0);
});

const timeoutPromise2 = new Promise((resolve, reject) => {
    setTimeout(resolve, 1000, 0);
});

const promisesToWait = [
    integerPromise,
    floatPromise,
    stringPromise,
    regularNumber,
    timeoutPromise1,
    timeoutPromise2
]

Promise.all(promisesToWait)
    .then((values) => {
        console.log(values);
    })
    .catch(error => console.error('Error has occurred:', error));

console.log('The Promise.all() method has been called.');

If you run the above code, the output will be like it's shown below.

developer@developer-pc:~/samples/javascript$ node index.js
Starting the application.
The Promise.all() method has been called.
Error has occurred: failure

Using the callbacks

In some cases, you may need to add callbacks to your code. They also may be used to build an asynchronous application. The perfect example is the setTimeout() method. It allows us to create a code that will be executed with some delay.

Please see the following sample, it shows how it should be used.

console.log('Starting the application.');

function delay(timeout, callback) {
    setTimeout(callback, timeout);
}

function timeoutCallback() {
    console.log('This is a callback.');
}

delay(1000, timeoutCallback);

console.log('The delay() method has been called.');

If you run the given code, you'll see that the "This is a callback." notice is the last in console output. That's because the callback was called asynchronously after the timer had elapsed.

developer@developer-pc:~/samples/javascript$ node index.js
Starting the application.
The delay() method has been called.
This is a callback.

Why using callbacks is not a good option

It may look like using the callbacks is a very simple solution. There is no need to create additional classes in the code. The asynchronous code may be created with just a single function.

However, the problem starts when you have many included callbacks. That issue is very well known to the web developers. You can create a "spaghetti code" that is hard to debug and maintain.

As a result, it's recommended to use the modern methods of making an asynchronous code. "Promises" should fulfill most of the needs. If you'd like to have a clean and compact script, then you may also use the await and async keywords in the code.

Conclusion

As you know, asynchronous code is very popular in JavaScript and TypeScript applications. It's heavily used in building dynamic and interactive elements on a web page. In many cases, the network communication is also handled with the help of asynchronous scripts.

Also, "promises" are a core functionality of a JavaScript application. From the first look, they may be very complicated. However, if you practice a lot, you may learn how to correctly create efficient asynchronous code.

Related Articles

Compiled code

How to convert a string to number in JavaScript or TypeScript

Big white clock

How to get a Unix timestamp in JavaScript or TypeScript language

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *