Minh họa trạng thái của Promise
Minh họa trạng thái của Promise

Tìm Hiểu Chi Tiết về Công Thức Promise trong JavaScript

Promise là một khái niệm quan trọng trong JavaScript hiện đại, giúp xử lý các hoạt động bất đồng bộ một cách hiệu quả và dễ quản lý hơn. Bài viết này sẽ đi sâu vào “Công Thức Promise” và cách sử dụng nó để viết code JavaScript sạch và dễ bảo trì.

I. Promise là gì và tại sao cần Promise?

Trong JavaScript, các tác vụ như gọi API, đọc file, hoặc thực hiện các tính toán phức tạp có thể mất một khoảng thời gian để hoàn thành. Các tác vụ này được gọi là “bất đồng bộ” (asynchronous). Trước khi có Promise, việc xử lý các tác vụ bất đồng bộ thường dựa vào callbacks, dẫn đến tình trạng “callback hell” – code lồng nhau phức tạp và khó đọc.

Promise ra đời để giải quyết vấn đề này, cung cấp một cách tiếp cận cấu trúc và dễ quản lý hơn cho các hoạt động bất đồng bộ. Promise đại diện cho một giá trị có thể chưa có sẵn tại thời điểm tạo, nhưng sẽ có trong tương lai.

II. Cấu trúc cơ bản của một Promise

Một Promise có thể ở một trong ba trạng thái:

  • Pending (chờ xử lý): Trạng thái ban đầu, biểu thị rằng hoạt động bất đồng bộ chưa hoàn thành.
  • Fulfilled (thành công): Hoạt động bất đồng bộ đã hoàn thành thành công và có một giá trị kết quả.
  • Rejected (thất bại): Hoạt động bất đồng bộ đã thất bại và có một lý do thất bại.

Để tạo một Promise, ta sử dụng constructor Promise:

const myPromise = new Promise((resolve, reject) => {
  // Thực hiện một hoạt động bất đồng bộ ở đây
  if (/* Hoạt động thành công */) {
    resolve(value); // Gọi resolve() khi thành công
  } else {
    reject(error); // Gọi reject() khi thất bại
  }
});

Trong đó:

  • resolve: Một hàm được gọi khi hoạt động bất đồng bộ thành công, nhận một giá trị làm đối số.
  • reject: Một hàm được gọi khi hoạt động bất đồng bộ thất bại, nhận một đối tượng Error làm đối số.

III. Sử dụng then(), catch()finally()

Để xử lý kết quả của một Promise, ta sử dụng các phương thức .then(), .catch().finally():

  • .then(onFulfilled, onRejected): Nhận hai hàm callback làm đối số. onFulfilled được gọi khi Promise được fulfilled, nhận giá trị trả về làm đối số. onRejected được gọi khi Promise bị rejected, nhận lý do thất bại làm đối số.
  • .catch(onRejected): Tương tự như .then(null, onRejected), được sử dụng để bắt các lỗi xảy ra trong Promise.
  • .finally(onFinally): Được gọi sau khi Promise được fulfilled hoặc rejected, không quan tâm đến kết quả. Thường được sử dụng để thực hiện các công việc dọn dẹp.
myPromise
  .then(value => {
    // Xử lý giá trị trả về khi thành công
    console.log("Thành công:", value);
  })
  .catch(error => {
    // Xử lý lỗi khi thất bại
    console.error("Lỗi:", error);
  })
  .finally(() => {
    // Thực hiện các công việc dọn dẹp
    console.log("Hoàn thành");
  });

IV. Promise Chaining

Một trong những ưu điểm lớn nhất của Promise là khả năng “chaining” (chuỗi hóa). Bạn có thể gọi .then() nhiều lần liên tiếp để thực hiện các thao tác tuần tự dựa trên kết quả của Promise trước đó.

fetch('https://api.example.com/data')
  .then(response => response.json())
  .then(data => {
    // Xử lý dữ liệu
    console.log(data);
    return data.items; // Trả về một Promise khác hoặc một giá trị để truyền cho .then() tiếp theo
  })
  .then(items => {
    // Xử lý items
    console.log(items);
  })
  .catch(error => {
    console.error("Lỗi:", error);
  });

Trong ví dụ trên, fetch() trả về một Promise. .then() đầu tiên chuyển đổi response thành JSON. .then() thứ hai xử lý dữ liệu JSON và trả về một mảng items. .then() thứ ba xử lý mảng items. Nếu bất kỳ Promise nào trong chuỗi bị rejected, .catch() sẽ được gọi.

V. Promise.all(), Promise.race(), Promise.allSettled()Promise.any()

JavaScript cung cấp các phương thức tĩnh để làm việc với nhiều Promise cùng một lúc:

  • Promise.all(promises): Nhận một mảng các Promise và trả về một Promise duy nhất. Promise này sẽ được fulfilled khi tất cả các Promise trong mảng đều được fulfilled, hoặc bị rejected ngay lập tức nếu có bất kỳ Promise nào bị rejected.
  • Promise.race(promises): Nhận một mảng các Promise và trả về một Promise duy nhất. Promise này sẽ được fulfilled hoặc rejected dựa trên Promise đầu tiên trong mảng được fulfilled hoặc rejected.
  • Promise.allSettled(promises): Nhận một mảng các Promise và trả về một Promise duy nhất. Promise này sẽ được fulfilled sau khi tất cả các Promise trong mảng đã được settled (fulfilled hoặc rejected). Kết quả trả về là một mảng các đối tượng, mỗi đối tượng mô tả trạng thái và giá trị/lý do của một Promise.
  • Promise.any(promises): Nhận một mảng các Promise và trả về một Promise duy nhất. Promise này sẽ được fulfilled với giá trị của Promise đầu tiên được fulfilled. Nếu tất cả các Promise đều bị rejected, Promise này sẽ bị rejected với một AggregateError chứa tất cả các lý do rejected.
const promise1 = Promise.resolve(1);
const promise2 = new Promise((resolve, reject) => setTimeout(resolve, 200, 'foo'));
const promise3 = new Promise((resolve, reject) => setTimeout(reject, 100, 'Error!'));

Promise.all([promise1, promise2])
  .then((values) => {
    console.log(values); // Output: [1, "foo"]
  })
  .catch(error => {
    console.error(error);
  });

Promise.race([promise2, promise3])
  .then((value) => {
    console.log(value); // Output: "foo"
  })
  .catch(error => {
    console.error(error);
  });

Promise.allSettled([promise1, promise2, promise3])
  .then((results) => console.log(results));
  // Output:
  // [
  //   { status: 'fulfilled', value: 1 },
  //   { status: 'fulfilled', value: 'foo' },
  //   { status: 'rejected', reason: 'Error!' }
  // ]

Promise.any([promise3, promise1])
  .then((value) => console.log(value))
  .catch(error => {
    console.error(error);
  }); // Output: 1

VI. Async/Await: Cú pháp đơn giản hơn cho Promise

Async/Await là một cú pháp mới được giới thiệu trong ES2017, giúp viết code bất đồng bộ dễ đọc và dễ viết hơn so với Promise. Async/Await thực chất là “đường cú pháp” (syntactic sugar) cho Promise.

Để sử dụng Async/Await, ta cần đánh dấu một hàm là async. Bên trong hàm async, ta có thể sử dụng từ khóa await để đợi một Promise được fulfilled.

async function fetchData() {
  try {
    const response = await fetch('https://api.example.com/data');
    const data = await response.json();
    console.log(data);
    return data;
  } catch (error) {
    console.error("Lỗi:", error);
  }
}

fetchData();

Trong ví dụ trên, await fetch() sẽ tạm dừng thực thi hàm cho đến khi Promise trả về từ fetch() được fulfilled. Giá trị trả về của Promise (response) sau đó được gán cho biến response. Tương tự, await response.json() sẽ tạm dừng thực thi cho đến khi Promise trả về từ response.json() được fulfilled.

VII. Xử lý lỗi với Async/Await

Với Async/Await, ta có thể sử dụng try...catch để xử lý lỗi một cách dễ dàng. Bất kỳ lỗi nào xảy ra trong khối try (bao gồm cả các lỗi trong các Promise được await) sẽ được bắt bởi khối catch.

VIII. Kết luận

“Công thức promise” là một phần không thể thiếu trong lập trình JavaScript hiện đại, giúp xử lý các hoạt động bất đồng bộ một cách hiệu quả và dễ quản lý. Nắm vững các khái niệm và kỹ thuật liên quan đến Promise là rất quan trọng để viết code JavaScript chất lượng cao. Async/Await cung cấp một cú pháp đơn giản hơn cho Promise, giúp code trở nên dễ đọc và dễ bảo trì hơn. Hãy thực hành thường xuyên để làm quen với Promise và Async/Await, và áp dụng chúng vào các dự án thực tế của bạn.

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply

Your email address will not be published. Required fields are marked *