Introduction to Promises

Promises are a fundamental concept in asynchronous programming in JavaScript and TypeScript. They represent a value that may be available now, or in the future, or never. Promises help manage asynchronous operations by providing a cleaner, more readable way to handle asynchronous code compared to traditional callback-based approaches.

Key Concepts

  1. Promise States:

    • Pending: Initial state, neither fulfilled nor rejected.
    • Fulfilled: Operation completed successfully.
    • Rejected: Operation failed.
  2. Promise Methods:

    • then(): Attaches callbacks for the resolution and/or rejection of the Promise.
    • catch(): Attaches a callback for only the rejection of the Promise.
    • finally(): Attaches a callback that is invoked when the Promise is settled (either fulfilled or rejected).

Creating a Promise

A Promise is created using the Promise constructor, which takes a function (executor) with two parameters: resolve and reject.

const myPromise = new Promise<number>((resolve, reject) => {
  const success = true; // Simulate an operation

  if (success) {
    resolve(42); // Operation succeeded
  } else {
    reject('Operation failed'); // Operation failed
  }
});

Using Promises

To handle the result of a Promise, you use the then, catch, and finally methods.

myPromise
  .then((value) => {
    console.log('Success:', value); // Output: Success: 42
  })
  .catch((error) => {
    console.error('Error:', error);
  })
  .finally(() => {
    console.log('Operation completed');
  });

Practical Example

Let's create a function that simulates fetching data from an API using Promises.

function fetchData(url: string): Promise<string> {
  return new Promise<string>((resolve, reject) => {
    setTimeout(() => {
      const success = true; // Simulate a successful API call

      if (success) {
        resolve(`Data from ${url}`);
      } else {
        reject('Failed to fetch data');
      }
    }, 2000);
  });
}

fetchData('https://api.example.com/data')
  .then((data) => {
    console.log('Fetched data:', data);
  })
  .catch((error) => {
    console.error('Error:', error);
  })
  .finally(() => {
    console.log('Fetch operation completed');
  });

Exercise

Create a function delayedMessage that returns a Promise. The Promise should resolve with a message after a specified delay.

Task

  1. Create a function delayedMessage that takes two parameters: message (string) and delay (number).
  2. The function should return a Promise that resolves with the message after the specified delay (in milliseconds).

Solution

function delayedMessage(message: string, delay: number): Promise<string> {
  return new Promise<string>((resolve) => {
    setTimeout(() => {
      resolve(message);
    }, delay);
  });
}

// Usage
delayedMessage('Hello, TypeScript!', 3000)
  .then((msg) => {
    console.log(msg); // Output after 3 seconds: Hello, TypeScript!
  });

Common Mistakes and Tips

  • Forgetting to handle errors: Always use catch to handle potential errors in Promises.
  • Chaining Promises incorrectly: Ensure that each then returns a Promise if you are chaining multiple asynchronous operations.
  • Using finally for cleanup: Use finally to perform cleanup actions regardless of whether the Promise was fulfilled or rejected.

Conclusion

In this section, we covered the basics of Promises in TypeScript, including how to create and use them. We also provided a practical example and an exercise to reinforce the concepts. Understanding Promises is crucial for handling asynchronous operations effectively, and they form the foundation for more advanced asynchronous patterns like async/await, which we will cover in the next topic.

© Copyright 2024. All rights reserved