Async/await

Async/await

Từ phiên bản ES2017, Javascript đã thêm hai từ khóa asyncawait. Đây là các cú pháp thay thế cho cú pháp cũ là constructor Promise(), hàm then(), catch(), còn bản chất Promise vẫn giữ nguyên.

Hàm async

Từ khóa async đặt trước hàm để chuyển hàm đó từ hàm đồng bộ sang thành hàm bất đồng bộ (async function). Hàm bất đồng bộ khi thực thi sẽ trả về một Promise.

Ví dụ về hàm async:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
async function hello() { return "Hello asyc" } // hàm thông thường
let hello1 = async function() { return "Hello express" }; // biểu thức hàm
let hello2 = async () => "Hello arrow"; // hàm mũi tên
class Person {
  async greeting() { // phương thức async
    return "Hello method";
  };
}
// sử dụng hàm bất đồng bộ
hello().then((message) => console.log(message)); // Hello asyc
hello1().then((message) => console.log(message)); // Hello express
hello2().then((message) => console.log(message)); // Hello arrow

// Hello method
new Person().greeting().then(message => console.log(message));

Trong ví dụ trên hàm hello() ở trên tương đương với:

1
2
3
function hello() {
  return Promise.resolve("Hello asyc");
}

Từ khóa await

Từ khóa await đặt trước biểu thức Promise để lấy giá trị trả về. Từ khóa await phải đặt trong hàm async. Khi gặp câu lệnh với từ khóa await, Promise phải thực thi xong mới chạy câu lệnh tiếp theo. Tức là các câu lênh await sẽ chuyển thực thi Promise bất đồng bộ thành chạy tuần tự.

Ví dụ đơn giản sử dụng await:

1
2
3
4
5
6
7
async function hello() {
  // đặt trước string sẽ chuyển string thành Promise
  let message = await "Hello asyc"; 
  return message;
};

hello().then((message) => console.log(message)); // Hello asyc

Ví dụ khác sử dụng await:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
// promise1 và promise2 là hàm trả về Promise
const promise1 = () => new Promise((resolve, reject) => {
  setTimeout(resolve, 500, 123);
});

const promise2 = () => new Promise((resolve, reject) => {
  setTimeout(resolve, 100, 456);
});

async function getNumber() {
  // promise1 chạy xong đến promise2 sẽ chạy
  // mất thời gian tổng cộng 600ms
  let firstNumber = await promise1();
  let secondNumber = await promise2();
  return firstNumber + secondNumber;
};
getNumber().then((number) => console.log(number)); // 579

Hàm setNumber() ở ví dụ trên có thể viết lại như sau:

1
2
3
4
5
6
7
function getNumber() {
  let firstNumber, secondNumber;
  return Promise.resolve()
    .then(() => promise1().then(number => firstNumber = number))
    .then(() => promise2().then(number => secondNumber = number))
    .then(() => firstNumber + secondNumber);
};

Chúng ta thấy rằng viết Promise theo kiểu async/await như ví dụ trước là dễ đọc và dễ hiểu tương tự cú pháp thông thường.

Quản lý lỗi với try-catch

Chúng ta sử dụng try-catch thay cho hàm catch() để quản lý lỗi với Promise sử dụng async/await.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
const promise1 = () => new Promise((resolve, reject) => {
  setTimeout(reject, 500, 123);
});

const promise2 = () => new Promise((resolve, reject) => {
  setTimeout(resolve, 100, 456);
});

async function getNumber() {
  try {
    let firstNumber = await promise1();
    let secondNumber = await promise2();
    return firstNumber + secondNumber;
  } catch (error) {
    console.error('Lỗi trong promise ' + error.toString()); // 123
  }
};
getNumber();

Chạy promise đồng thời với Promise.all()

Toán tử await sẽ chạy các promise tuần tự, để chạy đồng thời hãy sử dụng hàm Promise.all

Ví dụ sau chạy đồng thời hai promise nên thời gian sẽ chạy ít hơn ví dụ trước 100ms.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
const promise1 = () => new Promise((resolve, reject) => {
  setTimeout(resolve, 500, 123);
});

const promise2 = () => new Promise((resolve, reject) => {
  setTimeout(resolve, 100, 456);
});

async function getNumber() {
  // chạy song song nên thời gian chạy là khoảng 500ms
  return Promise.all([promise1(), promise2()])
    .then(([firstNumber, secondNumber]) => firstNumber + secondNumber)
};
getNumber().then((number) => console.log(number)); // 579