Altcademy - a Forbes magazine logo Best Coding Bootcamp 2023

How to wait for a function to finish in JavaScript

Understanding Asynchronous Behavior in JavaScript

Before we dive into how to make JavaScript wait for a function to finish, it's important to understand that JavaScript is an asynchronous programming language. What does that mean? Well, imagine you're in a restaurant. In a synchronous world, the waiter would take your order, go to the kitchen, wait for your food to be cooked, bring it to you, and only then take the next table's order. In an asynchronous world, the waiter takes your order, then immediately takes the next table's order, then the next, all while the kitchen is preparing the food. As each dish is ready, the waiter delivers it to the appropriate table. JavaScript behaves like the waiter in an asynchronous restaurant, juggling many tasks at once.

Let's Meet Promises

In JavaScript, one of the ways we can deal with this asynchronous behavior is using something called Promises. Think of a Promise like ordering a product online. When you place the order, you get a Promise that your product will be delivered. That Promise is an object representing the eventual completion or failure of an asynchronous operation.

Here's how we create a Promise in JavaScript:

let myPromise = new Promise((resolve, reject) => {
  let condition = /* some condition */

  if(condition) {
    resolve('Promise is kept!');
  } else {
    reject('Promise is broken');
  }
});

The Promise constructor takes a function as an argument. That function takes two parameters: resolve and reject, which are both functions. If everything goes well, we call the resolve function and pass the result. If something goes wrong, we call the reject function and pass an error message.

Making JavaScript Wait Using .then()

So, how can we make JavaScript wait for a function to finish using Promises? The answer is the .then() method. Think of .then() as the online tracking for your product order. Once the order (or Promise) is fulfilled, .then() gets called.

Here's a simple example:

myPromise
  .then(result => {
    console.log(result);  // This will output: 'Promise is kept!'
  })
  .catch(error => {
    console.log(error);  // This will output: 'Promise is broken'
  });

We attach .then() to our Promise, and it waits for the Promise to resolve before it runs. If the Promise is rejected, .catch() runs instead.

Waiting for Multiple Promises

What if we have multiple asynchronous tasks that we want to wait for? JavaScript has us covered with Promise.all(). Think of Promise.all() like ordering several products from an online store. You don't care about tracking each item individually. You just want to know when all your stuff arrives.

Here's how we can use Promise.all():

let promise1 = new Promise((resolve, reject) => { /* some code */ });
let promise2 = new Promise((resolve, reject) => { /* some code */ });

Promise.all([promise1, promise2])
  .then(results => {
    console.log(results[0]);  // Output from promise1
    console.log(results[1]);  // Output from promise2
  })
  .catch(error => {
    console.log(error);
  });

Promise.all() takes an array of Promises and returns a new Promise that resolves when all the input Promises have resolved. It's like a super waiter who can serve multiple tables at the same time!

The Modern Way: Async/Await

There's a more modern way to handle asynchronous behavior in JavaScript: the async/await syntax. It's like ordering takeout, then sitting on your couch and doing nothing until your food arrives. The async keyword tells JavaScript that a function is asynchronous. Inside that function, the await keyword makes JavaScript pause and wait for a Promise to resolve or reject.

Here's an example:

async function myAsyncFunction() {
  try {
    let result = await myPromise;
    console.log(result);
  } catch (error) {
    console.log(error);
  }
}

myAsyncFunction();

In this example, JavaScript waits for myPromise to resolve before moving on to the console.log(result) line. If myPromise rejects, the catch block runs instead.

Conclusion

Asynchronous behavior is like a double-edged sword in JavaScript. It can make our applications faster and more efficient, but it can also lead to confusion and bugs if not handled properly. However, with the power of Promises and the elegance of async/await, we can write clean, easy-to-understand code that handles asynchronous tasks with grace. Remember, mastering asynchronicity is like mastering the art of being a good waiter. It's all about taking the orders (tasks), then serving the food (results) when it's ready, all while providing a great user experience.