Door 05 | JS Adventskalender
Skip to content

Door 05

Published: at 07:00 AMSuggest Changes

Promises – Better Handling of Asynchronous Code

Asynchronous programming is an essential part of JavaScript, especially for operations like network requests, file system access, or timers. Before the introduction of Promises in ES6 (ECMAScript 2015), asynchronous operations were primarily handled with Callbacks, which often led to confusing and difficult-to-maintain code (known as “Callback Hell”). Promises offer a more elegant and structured method to deal with asynchronous code.

What are Promises?

A Promise is an object that represents a value that may be available now, later, or never. It is a placeholder for the result of an asynchronous operation. A Promise can be in one of three states:

A Simple Example

Suppose we have a function that fetches data from an API:

function fetchData(callback) {
  setTimeout(() => {
    const data = { name: 'Max', age: 30 };
    callback(data);
  }, 1000);
}

fetchData((data) => {
  console.log(data);
});

With callbacks, this code can quickly become confusing in more complex scenarios.

With Promises, we can write the same code more structurally:

function fetchData() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      const data = { name: 'Max', age: 30 };
      resolve(data);
      // In case of an error, you would call reject(error)
    }, 1000);
  });
}

fetchData()
  .then((data) => {
    console.log(data); // { name: 'Max', age: 30 }
  })
  .catch((error) => {
    console.error('Error:', error);
  });

Structure of a Promise

A Promise is created with the new Promise constructor, which accepts a function (executor). This function receives two arguments:

Example:

const myPromise = new Promise((resolve, reject) => {
  // Asynchronous operation
  const success = true;

  if (success) {
    resolve('Operation successful!');
  } else {
    reject('Operation failed!');
  }
});

Using then and catch

Promises provide the then and catch methods to respond to resolution or rejection.

Example:

myPromise
  .then((message) => {
    console.log(message); // 'Operation successful!'
  })
  .catch((error) => {
    console.error(error); // 'Operation failed!'
  });

Chaining Promises

One of the great advantages of Promises is the ability to chain them. This enables sequential execution of asynchronous operations.

Example:

function firstOperation() {
  return new Promise((resolve) => {
    setTimeout(() => {
      console.log('First operation completed.');
      resolve(1);
    }, 1000);
  });
}

function secondOperation(result) {
  return new Promise((resolve) => {
    setTimeout(() => {
      console.log('Second operation completed.');
      resolve(result + 1);
    }, 1000);
  });
}

firstOperation()
  .then((result) => {
    return secondOperation(result);
  })
  .then((finalResult) => {
    console.log('Final result:', finalResult); // Final result: 2
  });

Parallelization with Promise.all

With Promise.all, multiple Promises can be executed in parallel. The method waits until all Promises are fulfilled, or returns an error if one fails.

Example:

const promise1 = new Promise((resolve) => setTimeout(() => resolve('Result 1'), 1000));
const promise2 = new Promise((resolve) => setTimeout(() => resolve('Result 2'), 2000));

Promise.all([promise1, promise2])
  .then((results) => {
    console.log(results); // ['Result 1', 'Result 2']
  })
  .catch((error) => {
    console.error('Error:', error);
  });

Error Handling

Error handling is significantly improved with Promises. You can use catch to catch errors, and this handling can occur across multiple chained then calls.

Example:

fetchData()
  .then((data) => {
    console.log('Data received:', data);
    return processData(data);
  })
  .then((processedData) => {
    console.log('Processed data:', processedData);
  })
  .catch((error) => {
    console.error('An error occurred:', error);
  });

If an error occurs in any of the previous steps, the catch block is executed.

Promise Methods

In addition to Promise.all, there are other useful methods:

Example with Promise.race:

const promise1 = new Promise((resolve) => setTimeout(() => resolve('Fast'), 500));
const promise2 = new Promise((resolve) => setTimeout(() => resolve('Slow'), 1000));

Promise.race([promise1, promise2])
  .then((result) => {
    console.log(result); // 'Fast'
  });

Conclusion

Promises have revolutionized the way we write asynchronous code in JavaScript. They provide a clear and structured method for handling asynchronous operations, facilitate error handling, and improve code readability. By understanding and effectively using Promises, you can develop more robust and maintainable applications.

Start using Promises in your code today and experience the difference in the quality and maintainability of your code!


Previous Post
Door 06
Next Post
Door 04