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 • 📖 7 min read

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.

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

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:

const first = () => console.log('1')
const second = () => console.log('2') 

first()
second()
Synchronous execution
Copied to clipboard!

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.

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

setTimeout(() => first(), 0)
second()
Asynchronous execution
Copied to clipboard!

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


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:

const first = callback => {
    console.log('one')
    callback()
}

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

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

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:

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
Copied to clipboard!

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:

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

document.addEventListener('click', onClick)
Event listeners also utilize callback functions
Copied to clipboard!

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 JavaScript

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:

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
Copied to clipboard!

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:

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
Copied to clipboard!

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:

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
Copied to clipboard!

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:

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
Copied to clipboard!

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:

(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
Copied to clipboard!
Looking to improve your skills? Check out our interactive course to master JavaScript from start to finish.
Master JavaScript

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
Did you find this page helpful?
📚 More Webtips
Frontend Course Dashboard
Master the Art of Frontend
  • check Access exclusive interactive lessons
  • check Unlimited access to hundreds of tutorials
  • check Remove ads to learn without distractions
Become a Pro

Recommended