How to Wait for a Function to Finish in JavaScript

How to Wait for a Function to Finish in JavaScript

3 different solutions with code examples
Ferenc Almasi β€’ 2023 August 24 β€’ Read time 7 min read
Learn how you can use callbacks, promises and async/await to wait for function to finish in JavaScript.
  • twitter
  • facebook
JavaScript

JavaScript is asynchronous by nature, and many times you'll need to work with non-linear code. This is when you'll run into cases where you need to wait for one function to finish to start another.

In this tutorial, we'll take a look at three different solutions to help you address this problem. But first, let's understand how asynchronous code behaves.


How Asynchronous Code Works

To demonstrate how to solve the problem, let's create the problem first. Take the following code as an example, where we have two functions called one after the other:

Copied to clipboard! Playground
const first = () => console.log('1')
const second = () => console.log('2') 

first()
second()
Synchronous execution

This code is synchronous. The execution order is from top to bottom, and as expected, "one" and "two" are logged to the console. If we change the order and move the second function call before the first, the order will change accordingly. However, if we modify the code as follows, the order will also change.

Copied to clipboard! Playground
const first = () => console.log('one')
const second = () => console.log('two') 

setTimeout(() => first(), 0)
second()
Asynchronous execution

Even though the setTimeout is set with a timeout of 0 milliseconds, the second function call is still executed before the first one.

This happens because the setTimeout function is pushed to the end of the call stack. As a result, all other function calls are executed first, regardless of the timeout duration.


Wait for Functions using Callbacks

The simplest way to resolve this is by using a callback function. Callbacks are functions passed as arguments to other functions, to be executed once an event has occurred or a task is finished.

To fix the previous example using a callback, we can pass the second function as an argument to the first function and call it at the very end of the first function, like so:

Copied to clipboard! Playground
const first = callback => {
    console.log('one')
    callback()
}

const second = () => console.log('two') 

setTimeout(() => first(second), 0)
Using callback to wait for execution

Now we've turned around the execution order to ensure that the second function is only called after the first function has been executed. By specifying the callback as an argument, we make the function flexible, allowing us to pass any function to be executed once the first function runs.

If you need to execute multiple functions after finishing one, the cleanest solution is to use an array as the callback argument. This way, you can use a forEach loop inside the original function to iterate through the other functions and call them sequentially:

Copied to clipboard! Playground
const first = callbacks => {
    console.log('one')
    callbacks.forEach(callback => callback())
}

const second = () => console.log('two')
const third = () => console.log('three')

setTimeout(() => first([second, third]), 0)
Using multiple callbacks

The callback functions will be executed in the order they appear in the array. Callbacks are also commonly used for event listeners. The following code also demonstrates the use of a callback function:

Copied to clipboard!
// This function will only run when the event occurs
const onClick = () => console.log('clicked')

document.addEventListener('click', onClick)
Event listeners also utilize callback functions

Avoid nesting multiple callback functions within each other, as this can result in code that's difficult to maintain.

It's worth mentioning that when you have multiple functions dependent on each other, using callbacks might not be the optimal solution. This is because they can lead to a situation known as "callback hell", where functions become deeply nested.

Looking to improve your skills? Check out our interactive course to master JavaScript from start to finish.
Master JavaScriptinfo Remove ads

Wait for Functions using Promises

The second solution for waiting until functions finish before executing another one involves the use of promises. Promises also help us avoid callback hell. They're also called thennables, because we can chain a callback function from them using a then function. Consider the following example:

Copied to clipboard! Playground
const first = () => {
    fetch('https://jsonplaceholder.typicode.com/posts/1')
        .then(response => response.json())
        .then(result => {
            console.log('one')
        })
}

const second = () => console.log('two')

first()
second()
Using promises in JavaScript

The Fetch API in JavaScript is promise-based. We chain two then callbacks from the fetch function:

  1. First, we convert the response object into JSON.
  2. Then, we consume the JSON response in the second then.

However, the problem with this approach is that the fetch function is asynchronous. The "two" is logged immediately, while "one" is only logged after the response is returned from the server.

To fix this code, we need to return the fetch call from the first function. This means the return value of the first function will be a promise, so we can chain a then callback and call the second function only when the first one has finished execution:

Copied to clipboard! Playground
const first = () => {
    return fetch('https://jsonplaceholder.typicode.com/posts/1')
        .then(response => response.json())
        .then(result => {
            console.log('one')
        })
}

const second = () => console.log('two')

first().then(() => {
    second()
})
Return the fetch from the first function

Now, let's consider a scenario where we have three different API calls, and we want to wait for all of them to finish before calling another function. This can be achieved using Promise.all, which expects an array of promises to be passed:

Copied to clipboard! Playground
const fetchPost = id => {
    return fetch('https://jsonplaceholder.typicode.com/posts/' + id)
        .then(response => response.json())
}

const first = fetchPost(1)
const second = fetchPost(2)
const third = fetchPost(3)

Promise.all([first, second, third]).then(results => {
    // Execute function here after all calls finished
    console.log(results);
});
Waiting for multiple concurrent calls

The functions will be resolved in order, meaning the results variable will be an array where the first item references the result of the first function, the second item references the result of the second function, and so on.


Wait for Functions using async/await

Lastly, we can also use the async/await keywords to wait for functions to finish execution before proceeding. This approach allows us to write asynchronous code in a synchronous manner, improving code readability and maintainability.

To rewrite the previous example using the async/await keywords, we can change the code in the following way:

Copied to clipboard! Playground
const fetchPost = id => {
    return fetch('https://jsonplaceholder.typicode.com/posts/' + id)
        .then(response => response.json())
}

const waitFor = async () => {
    const first = await fetchPost(1)
    const second = await fetchPost(2)
    const third = await fetchPost(3)

    // Execute any remaining functions here

    console.log(first, second, third)
}

waitFor()
Using async/await in JavaScript

To use the await keyword, we must mark the function as asynchronous using the async keyword. Now these functions are executed in the order they appear in the code. Of course, if we want to avoid creating an extra function just to use await in JavaScript, we can also use an IIFE:

Copied to clipboard! Playground
(async () => {
    const first = await fetchPost(1)
    const second = await fetchPost(2)
    const third = await fetchPost(3)

    // Execute any remaining functions here

    console.log(first, second, third)
})()
Using an IIFE for async/await

Conclusion

In conclusion, waiting for functions to finish execution can be done by either using callbacks, promises, or the async/await keyword. So, which one should you use?

  • async/await: Use async/await as the primary choice for writing code in a synchronous manner. This improves read- and maintainability.
  • Promises: When dealing with multiple concurrently executing calls, use Promise.all or Promise.race.
  • Callbacks: Only use callback for simple cases or when using async/await or promises are not applicable.

Is there anything you think this tutorial is missing? Let us know in the comments below! If you're interested in learning more about JavaScript, make sure to check out our roadmap below. Thank you for reading, happy coding! πŸ‘¨β€πŸ’»

Master JavaScript
  • twitter
  • facebook
JavaScript
Did you find this page helpful?
πŸ“š More Webtips
Frontend Course Dashboard
Master the Art of Frontend
  • check Access 100+ interactive lessons
  • check Unlimited access to hundreds of tutorials
  • check Prepare for technical interviews
Become a Pro

Courses

Recommended

This site uses cookies We use cookies to understand visitors and create a better experience for you. By clicking on "Accept", you accept its use. To find out more, please see our privacy policy.