Computer >> Hướng Dẫn Máy Tính >  >> Lập Trình >> JavaScript

Cách sử dụng JavaScript Promises

Viết mã không đồng bộ (asynchronous) từng là một nỗi đau trong JavaScript. Để viết các hoạt động không đồng bộ trong mã của bạn, bạn phải đối mặt với nhiều cấp độ các hàm callback (hàm gọi lại). Càng nhiều hàm bạn đưa vào mã, càng khó đọc.

Trong ES6, Promises đã đến giải cứu. Promises là một cách để viết mã không đồng bộ hiệu quả trong JavaScript.

Trong hướng dẫn này, chúng ta sẽ nói về Promise là gì và cách chúng hoạt động. Chúng ta sẽ đi qua một ví dụ về Promise để giúp bạn học cách sử dụng chúng trong mã của mình. Hãy bắt đầu!

Promise là gì?

Một Promise là một đối tượng trả về một phản hồi mà bạn muốn nhận trong tương lai.

Một cách tốt để nghĩ về JavaScript Promises là so sánh chúng với cách con người hứa hẹn. Khi bạn hứa, đó là một sự đảm bảo rằng bạn sẽ làm một việc gì đó vào một ngày trong tương lai. Bạn không làm việc đó ngay bây giờ; bạn sẽ làm nó tại một thời điểm sau.

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

  • Pending (Đang chờ): Promise chưa được hoàn thành.
  • Rejected (Bị từ chối): Promise không thể trả về giá trị.
  • Fulfilled (Đã thực hiện/Resolved): Promise đã được hoàn thành.

Điều này tương tự như lời hứa trong đời thực. Bạn có thể có một lời hứa đang chờ mà bạn nói bạn sẽ thực hiện trong tương lai. Bạn có thể thực hiện lời hứa. Bạn có thể từ chối, hoặc "phá vỡ" lời hứa, và không thực hiện theo những gì bạn đã đồng ý làm.

Khi bạn tạo một Promise, nó sẽ ở trạng thái pending. Promise sẽ tồn tại ở trạng thái này cho đến khi nhận được phản hồi và Promise được fulfilled hoặc rejected.

Cách tạo JavaScript Promise

Chúng ta sẽ bắt đầu bằng cách tạo một Promise trả về tên của người dùng. Hàm này sẽ trả về tên người dùng cho chương trình của chúng ta sau ba giây. Điều này sẽ giúp chúng ta thấy cách Promises được dùng để viết mã không đồng bộ.

Để tạo một Promise, chúng ta cần tạo một đối tượng Promise:

new Promise((resolve, reject) => { 
  // Mã của bạn ở đây 
});

Promises chấp nhận hai đối số: một hàm xử lý thành công của Promise và một hàm xử lý Promise thất bại. Điều này có nghĩa là Promise của chúng ta sẽ trả về các giá trị khác nhau tùy thuộc vào kết quả.

Một Promise nên chứa một câu lệnh "if" xác định xem Promise có thực thi thành công hay không. Nếu Promise không thực thi thành công, nó nên trả về một Promise ở trạng thái rejected:

new Promise((resolve, reject) => { 
  if (giá_trị_là_true) { 
    resolve(); 
  } else { 
    reject(); 
  } 
});

Mở một file JavaScript mới và sao chép mã sau:

let returnName = new Promise((resolve, reject) => { 
  let name; 
  setTimeout(() => { 
    name = "Cinnamon"; 
    if (name === "Cinnamon") { 
      resolve(name); 
    } else { 
      reject("Promise này đã thất bại."); 
    } 
  }, 3000); 
});

Mã này trả về tên Cinnamon cho chương trình chính của chúng ta khi chúng ta gọi Promise. Mã này mất ba giây để chạy, hoặc 3.000 mili giây. Điều này có nghĩa là khi chúng ta gọi Promise của mình, nó sẽ ở trạng thái pending trong ba giây. Sau khi Promise được giải quyết (settled), một Promise resolved hoặc rejected sẽ được trả về cho chương trình chính.

Nếu "name" bằng "Cinnamon", Promise của chúng ta được resolved thành công; ngược lại, Promise của chúng ta sẽ bị rejected. Trong ví dụ này, "name" được đặt là "Cinnamon". Vì chúng ta đã chỉ định một tên, Promise của chúng ta tự resolve.

then() và catch() với Promises

Mã của chúng ta chưa thực thi. Chúng ta phải gọi đối tượng Promise của mình:

returnName.then(data => { 
  console.log(data); 
});

Từ khóa then() cho phép chúng ta truy xuất phản hồi từ Promise của mình. Nó được gọi sau khi Promise được resolved. Trong ví dụ này, chúng ta in nội dung của data, là phản hồi được trả về từ Promise, ra console.

Mã của chúng ta trả về: Cinnamon.

then() chỉ xử lý các Promise thành công. Điều gì xảy ra nếu Promise của chúng ta bị rejected? Đó là nơi phương thức catch() xuất hiện.

Bạn có thể khai báo nhiều câu lệnh then() bên trong một Promise. Điều này được gọi là Promise chaining (x chuỗi Promise). Hãy dùng một ví dụ để minh họa cách chaining hoạt động:

returnName.then(data => { 
  console.log(data); 
}).then(() => { 
  console.log("Hàm này đã kết thúc!"); 
});

Mã này sẽ trả về:

Cinnamon
Hàm này đã kết thúc.

Các câu lệnh then() được thực thi theo thứ tự chúng được chỉ định trong Promise constructor.

Phương thức catch() được gắn vào Promise giống như phương thức then(). Hãy tạo một handler quản lý Promise bị rejected cho ví dụ tên của chúng ta:

returnName.then(data => { 
  console.log(data); 
}).catch(error => { 
  console.log(error); 
});

Chúng ta đã gắn một câu lệnh catch() sau câu lệnh then(). Lưu ý rằng chúng ta sử dụng dấu chấm phẩy (;) ở cuối mã Promise. Bạn không nên chỉ định dấu chấm phẩy sau phương thức then() nếu bạn đang sử dụng catch(). Điều này sẽ nói cho JavaScript rằng then() và catch() là riêng biệt, và do đó một lỗi sẽ được trả về trong mã của bạn.

Nếu Promise của chúng ta bị rejected, nội dung của câu lệnh catch() sẽ chạy.

Hãy xem điều gì xảy ra khi chúng ta chạy mã của mình:

Cinnamon

Giá trị Cinnamon được trả về vì Promise của chúng ta resolve thành công. Nếu giá trị của "name" không phải là "Cinnamon" trong Promise của chúng ta, một lỗi sẽ được trả về:

Uncaught (in promise) This promise has failed.

Chúng ta đã chỉ định thông báo lỗi: "This promise has failed." trong hàm reject() bên trong Promise của mình.

finally() với Promises

Điều gì xảy ra nếu bạn muốn thực thi một khối mã sau khi một Promise đã thực thi, bất kể Promise thành công hay thất bại?

Đó là nơi câu lệnh finally() tỏa sáng. Câu lệnh finally() chạy cho dù Promise được fulfilled hay rejected. Hãy cập nhật lời gọi Promise của chúng ta để sử dụng câu lệnh finally:

returnName.then(data => { 
  console.log(data); 
}).catch(error => { 
  console.log(error); 
}).finally(() => { 
  console.log("Promise returnName đã được thực thi."); 
});

Mã của chúng ta trả về:

Cinnamon
Promise returnName đã được thực thi.

Promise của chúng ta trả về Cinnamon vì nó thành công. Nội dung của câu lệnh finally() sau đó được thực thi, trả về một thông báo khác cho console của chúng ta.

Kết luận

JavaScript Promises cho phép bạn viết mã không đồng bộ sạch sẽ.

Trong hướng dẫn này, chúng ta đã bao gồm các nguyên tắc cơ bản của Promises. Còn rất nhiều thứ về Promises hơn những gì chúng ta đã bao gồm trong hướng dẫn này. Bước tiếp theo trong hành trình học tập của bạn là viết một vài Promises trong mã của chính bạn.

Dưới đây là một thách thức: viết một Promise thực hiện một yêu cầu web (web request) và trả về phản hồi của nó. Để làm điều này, bạn sẽ muốn sử dụng API fetch() để thực hiện yêu cầu. Yêu cầu này nên được bao bọc trong một Promise. Bạn nên viết mã xử lý Promise cả khi nó thành công và khi nó thất bại.

Nếu bạn muốn đi xa hơn, hãy xem các hàm async (async functions) trong JavaScript. Chúng có thể được sử dụng để viết mã không đồng bộ và thường được sử dụng cùng với Promises.

Bây giờ bạn đã sẵn sàng để bắt đầu viết JavaScript Promises như một chuyên gia!