Async/await Trong JavaScript

Async / await được giới thiệu bởi ES7 là một cải tiến từ promise mang đến điều tuyệt vời trong Asynchronous programming với JavaScript. Nó cung cấp cho các developer js viết mã đồng bộ (synchronous) trên những chức năng không đồng bộ (asynchonous), mà không làm gián đoạn code của chính mình. Tuy nhiên không hẳn những developer nào cũng có thể sử dụng chúng một cách hiệu quả và sâu hơn nữa là hiểu cốt lõi của việc sử dụng chúng. Trong bài viết này chúng ta sẽ cùng nhau khám phá về của async / await qua các ví dụ.

Async / Await là một tính năng của JavaScript giúp chúng ta làm việc với các hàm bất đồng bộ theo cách thú vị hơn và dễ hiểu hơn. Nó được xây dựng trên promise và tương thích với tất cả các promise dựa trên API. Đầu tiên chúng ta hãy bắt đầu với async trước.

Async functions

Async functions là hàm bất đồng bộ. Nó có thể được sử dụng như sau:

async function f() {
  return 1;
}

Một function luôn trả về một promise. Các giá trị khác được bao bọc trong một promise đã được giải quyết tự động. Ví dụ, hàm này trả về một promise đã giải quyết với kết quả là 1

async function f() {
  return 1;
}

f().then(alert); // 1

Chúng ta có thể trả lại một promise theo cách khác như sau:

async function f() {
  return Promise.resolve(1);
}

f().then(alert); // 1

Vì vậy, hãy đảm bảo rằng hàm async trả về một promise. Nhưng không chỉ có vậy. Chúng ta có thể dùng await. Nó chỉ hoạt động bên trong các async functions.

Await

Cú pháp:

// works only inside async functions
let value = await promise;

 await khi được đặt trước một promise, nó sẽ đợi cho đến khi promise kết thúc và trả về kết quả. Dưới đây là một ví dụ với một promise sẽ được xử lí trong 1 giây:

async function f() {

  let promise = new Promise((resolve, reject) => {
    setTimeout(() => resolve("done!"), 1000)
  });

  let result = await promise; // wait until the promise resolves (*)

  alert(result); // "done!"
}

f();

Hiểu await theo đúng nghĩa đen thì ban đầu nó sẽ tạm dừng việc thực thi hàm cho đến khi promise được giải quyết, và sau đó nó sẽ tiếp tục khi promise đó được xử lí xong.  Điều đó không tốn bất kỳ tài nguyên CPU nào, vì JavaScript có thể thực hiện các công việc khác trong thời gian chờ đợi: thực thi các tập lệnh khác, xử lý các sự kiện, v.v.

Error handling

Nếu một promise được xử lí bình thường, thì await promise sẽ trả về kết quả. Nhưng trong trường hợp bị từ chối, nó sẽ tạo ra lỗi, giống như thể có một câu lệnh throw ở dòng đó. Ví dụ

async function f() {
  await Promise.reject(new Error("Whoops!"));
}

Nó sẽ bị lỗi như dưới đây trong trường hợp bị từ chối:

async function f() {
  throw new Error("Whoops!");
}

Trong các tình huống thực tế, promise có thể mất một thời gian trước khi bị từ chối. Trong trường hợp đó sẽ có một khoảng thời gian trễ trước khi await sinh ra một lỗi. Một điều tuyệt vời khác về Async / Await là nó cho phép chúng ta bắt các lỗi không mong đợi bằng cách sử dụng try / catch

async function f() {

  try {
    let response = await fetch('http://no-such-url');
  } catch(err) {
    alert(err); // TypeError: failed to fetch
  }
}

f();
  • Mệnh đề catch sẽ xử lý các lỗi gây ra bởi các hàm bất đồng bộ hoặc bất kỳ lỗi nào chúng ta có thể đã viết bên trong khối try.
async function f() {

  try {
    let response = await fetch('/no-user-here');
    let user = await response.json();
  } catch(err) {
    // catches errors both in fetch and response.json
    alert(err);
  }
}

f();

Nếu chúng ta không có try..catch, thì promise được tạo bởi lệnh gọi của hàm bất đồng bộ f() sẽ bị từ chối. Vì vậy chúng ta cần thêm .catch vào để xử lý nó:

async function f() {
  let response = await fetch('http://no-such-url');
}

// f() becomes a rejected promise
f().catch(alert); // TypeError: failed to fetch // (*)

Nếu chúng ta quên thêm vào .catch đó, thì chúng ta sẽ gặp lỗi promise. Chúng ta có thể bắt gặp các lỗi như vậy bằng cách sử dụng unhandled rejection như được mô tả trong bài Error handling with promise

Tổng kết

 Việc sử dụng async/await có một số ưu điểm vượt trội:

  • Code dễ đọc hơn rất rất nhiều, không cần phải then rồi catch, chỉ viết như bình thường, sau đó dùng try/catch để bắt lỗi.
  • Debug dễ hơn nhiều, vì mỗi lần dùng await được tính là một dòng code, do đó ta có thể đặt debugger để debug từng dòng như thường.
  • Khi có lỗi, exception sẽ chỉ ra lỗi ở dòng số mấy chứ không chung chung là un-resolved promise.
  • Với promise hoặc callback, việc kết hợp if/else hoặc retry với code asynchnous là một điều gì đó khá khó chịu. Với async/await, việc này vô cùng dễ dàng.

Vậy là series tìm hiểu về promise,async/ await của chúng ta đã khép lại. Các bạn truy cập Fanpage của Suntech để tìm kiếm các thông tin về khóa học và việc làm tại đây. Hẹn gặp lại các bạn trong các bài học khác!

Trần Quang Hào
SUNTECH VIỆT NAM   Đăng ký để nhận thông báo mới nhất