Hi 🤓 Cảm ơn bạn đã ghé thăm blog này, nếu những bài viết trên blog giúp ích cho bạn. Bạn có thể giúp blog hiển thị quảng cáo bằng cách tạm ngừng ad blocker 😫 và để giúp blog duy trì hoạt động nếu bạn muốn.
Cảm ơn bạn!

Trong chủ đề bất đồng bộ (asynchronous) JavaScript của học JavaScript cơ bản thì chúng ta đã tìm hiểu về một số kiến thức như: setTimeout() trong JavaScript là gì?, Callback Function trong JavaScript là gì? tại sao lại được sử dụng nhiều như vậy?,... hôm nay mình xin giới thiệu đến các bạn bài viết về Promise trong JavaScript nhé!

Trong JavaScript, promise dùng để xử lý những vấn đề liên quan đến asynchronous hay bất đồng bộ. Nếu không xử lý bất đồng bộ thì các đoạn code chúng ta viết sẽ không chạy đúng trình tự và như vậy có thể dẫn đến các lỗi không mong muốn 😀.

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

  1. Pending
  2. Fulfilled
  3. Rejected

Promise sẽ bắt đầu ở trạng thái pending (chờ xử lý). Điều đó có nghĩa là quá trình này chưa hoàn thành. Nếu xử lý thành công, quá trình sẽ kết thúc ở trạng thái fulfilled (hoàn thành). Nếu xảy ra lỗi, quá trình sẽ kết thúc ở trạng thái rejected.

Ví dụ: khi muốn lấy thông tin bài viết từ blog homiedev.com, chúng ta sẽ yêu cầu dữ liệu từ server bằng cách sử dụng một promise, server sẽ xử lý request và promise lúc này sẽ ở trạng thái pending. Khi dữ liệu trả về thành công, promise sẽ chuyển thành trạng thái fulfilled. Nếu có lỗi xảy ra, thì nó sẽ ở trạng thái rejected.

Sau đây chúng ta cùng xem cách tạo một promise nhé 😀.

Tạo Promise trong JavaScript

Để tạo một đối tượng promise, chúng ta sẽ sử dụng constructor Promise().

let promise = new Promise(function(resolve, reject){
     // ...
});

Constructor Promise() nhận một function làm đối số. function này có hai đối số là hai hàm resolve()reject().

Nếu promise trả về kết quả thành công, hàm resolve() được gọi. Nếu có lỗi xảy ra, hàm reject() được gọi.


Lý thuyết nhiều rồi 😋, bây giờ chúng ta cùng xem một ví dụ để dễ hiểu hơn nhé ^^:

let promise = new Promise(function(resolve, reject) {
  // sau 1 giây trả kết quả báo rằng công việc đã xử lý xong và kết quả là "done"
  setTimeout(() => resolve("done"), 1000);
});

Khi chạy đoạn code trên:

  1. function trong Promise sẽ được gọi tự động và ngay lập tức (khi sử dụng new Promise).
  2. function trong Promise nhận hai đối số: resolvereject. Các hàm này được xác định trước bởi JavaScript engine, vì vậy chúng ta không cần tạo chúng.

Ở ví dụ trên, mình giả sử chúng ta thực hiện một công việc mất khoảng 1s, sau 1s xử lý, resolve("done") sẽ được thực thi. Điều này làm thay đổi trạng thái của promise:

  1. Lời hứa (promise) lúc này "đã được thực hiện", trạng thái chuyển thành fulfilled.
new Promise() Thực thi: resolve("done") Chuyển trạng thái
state: "pending" 👉👉 state: "fulfilled"
result: undefined 👉👉 result: "done"

Nếu lời hứa (promise) của chúng ta bị từ chối (reject) thì sao ❔ chúng ta cùng đến với ví dụ:

let promise = new Promise(function(resolve, reject) {
  // sau 1 giây, thông báo rằng công việc đã xử lý xong nhưng không thành công
  setTimeout(() => reject(new Error("Xảy ra lỗi rồi 😢!")), 1000);
});

Lúc này trạng thái promise sẽ chuyển thành:

new Promise() Thực thi: reject(error) Chuyển trạng thái
state: "pending" 👉👉 state: "rejected"
result: undefined 👉👉 result: Error: Xảy ra lỗi rồi 😢!

Tóm lại, chúng ta thực hiện một công việc (thường là một công việc cần thời gian) và sau đó gọi resolve hoặc reject để thay đổi trạng thái của promise.

Một lời hứa đã resolved hoặc rejected được gọi là "settled" (đã giải quyết), trái ngược với lời hứa ban đầu "pending".

Các thuộc tính stateresult của một object Promise là internal (bên trong Promise mới sử dụng được). Chúng ta không thể truy cập trực tiếp để sử dụng chúng. Để có thể truy cập thuộc tính của Promise, chúng ta có thể sử dụng các method .then, .catch, .finally.

then(), catch() trong JavaScript

Method then() trong JavaScript nhận hai đối số: các callback function cho trường hợp thành công và thất bại của promise.

Bạn có thể đọc bài viết về callback function tại đây: Callback Function trong JavaScript là gì? tại sao lại được sử dụng nhiều như vậy?.

Cú pháp của method then() là:

promiseObject.then(
  (value) => { /* xử lý nếu thành công */ },
  (reason) => { /* xử lý nếu lỗi tại đây */ },
);

Trong đó:

  1. Đối số đầu tiên của .then là một hàm chạy khi promise được resolved và nhận kết quả.
  2. Đối số thứ hai của .then là một hàm chạy khi promise bị rejected và nhận được lỗi.

Ví dụ:

Hiển thị kết quả nhận được từ promise đã resolved.

let promise = new Promise(function(resolve, reject) {
  setTimeout(() => resolve("Thành công!"), 1000);
});

promise.then(
  result => alert(result), // hiện "Thành công!" sau 1 giây
  error => alert(error) // không chạy vì hàm resolve được gọi
);

Nếu quá trình promise xử lý bị lỗi, ví dụ:

let promise = new Promise(function(resolve, reject) {
  setTimeout(() => reject(new Error("Lỗi mất rồi 😢!")), 1000);
});

promise.then(
  result => alert(result), // không chạy vì reject được gọi
  error => alert(error) // hiện lỗi Error: Lỗi mất rồi 😢! sau 1 giây
);

Nếu chỉ quan tâm đến việc hoàn thành thành công, thì chúng ta có thể cung cấp một đối số cho .then:

let promise = new Promise(resolve => {
  setTimeout(() => resolve("Thành công!"), 1000);
});

promise.then(alert); // hiện "Thành công!"

Chúng ta có thể xử lý công việc từng phân đoạn bằng cách sử dụng nhiều .then() như sau:

let countValue = new Promise(function (resolve, reject) {
  resolve("Xử lý thành công!");
});

countValue
  .then(function successValue(result) {
    return result;
  })
  .then(function successValue1(result) {
    return result + ' 😁';
  })
  .then(function successValue2(result) {
    console.log(result); // Xử lý thành công! 😁
  });

Nếu chúng ta chỉ quan tâm đến xử lí lỗi, thì chúng ta có thể sử dụng null làm đối số đầu tiên: .then(null, errorHandlingFunction). Hoặc chúng ta có thể sử dụng method .catch(errorHandlingFunction):

let promise = new Promise((resolve, reject) => {
  setTimeout(() => reject(new Error("Lỗi mất rồi!")), 1000);
});

// sử dụng .catch(f) tương đương với promise.then(null, f)
promise.catch(alert); // hiện lỗi Error: Lỗi mất rồi! sau 1 giây

Sử dụng .catch(f) cũng giống như .then (null, f), nó chỉ là một cách viết tắt (shorthand) 😁.

finally trong JavaScript

Trong promise cũng có finally giống như trong try {...} catch {...}

Các bạn có thể đọc bài viết về try {...} catch {...} tại: Try Catch Javascript là gì?.

Method finally() có thể hữu ích khi chúng ta muốn thực hiện một số xử lý sau khi promise đã được giải quyết xong (settled), bất kể kết quả của nó như thế nào.

Ví dụ. dừng các loading, đóng các kết nối không cần thiết, ...

new Promise((resolve, reject) => {
  setTimeout(() => resolve("Đây là blog homiedev.com"), 2000);
})
  .finally(() => alert("Promise ready")) // chạy phần này trước
  .then(result => alert(result)); // sau đó .then sẽ hiện "Đây là blog homiedev.com"

Tóm lại, đây là những lưu ý về finally các bạn cần nắm ^^:

  1. finally không nhận được kết quả của hàm xử lý trước (nó không có đối số). Thay vào đó, kết quả này sẽ được chuyển tới hàm xử lý phù hợp tiếp theo.
  2. Nếu finally return một cái gì đó, nó sẽ bị bỏ qua.
  3. Khi finally throw một lỗi, thì quá trình thực thi sẽ chuyển đến hàm xử lý lỗi gần nó nhất.

Như vậy là chúng ta đã tìm hiểu cơ bản về promise trong JavaScript. Những vấn đề liên quan khác chúng ta sẽ tìm hiểu trong các bài viết sau nhé ^^.

Hi vọng bài viết giúp ích cho các bạn. Nếu có thắc mắc chúng ta cùng thảo luận bên dưới phần bình luận nhé 😉.

Có thể bạn thích ⚡
homiedev
About Me

Hi, I'm @devnav. Một người thích chia sẻ kiến thức, đặc biệt là về Frontend 🚀. Trang web này được tạo ra nhằm giúp các bạn học Frontend hiệu quả hơn 🎉😄.

Chúc các bạn tìm được kiến thức hữu ích trong blog này 😁😁.