Syntax

Hai từ khóa asyncawait xuất hiện ở ES8 giúp xử lý các tác vụ bất đồng bộ tốt hơn. Đồng thời, chúng cũng giúp đoạn code trông dễ đọc và dễ hiểu hơn so với code được viết bằng promise thuần.

Tuy nhiên, asyncawait vẫn hoạt động xoay quanh các promise.

Async

Từ khóa async đứng trước một function khiến cho function đó trả về một promise. Trong đoạn code dưới, hàm square sẽ trả về một promise thay vì trả về giá trị.

async function square(n) {
  return n * n
}

Đoạn code trên tương đương với:

async function square(n) {
  return Promise.resolve(n * n)
}

Khi xuất giá trị trả về của square, ta sẽ nhận được kết quả như sau:

console.log(square(2)) // Promise {<fulfilled>: 4}

Await

Xét ví dụ sau:

function squareOf(n) {
  return n * n
}
 
function cubeOf(n) {
  let square = 0
 
  setTimeout(() => {
    square = squareOf(n)
  }, 3000)
 
  console.log(square * n) // 0
}
 
cubeOf(3)

Tác vụ square = squareOf(n) là một tác vụ bất đồng bộ, sẽ được thực hiện sau 3 giây. Do đó, square sẽ mang giá trị là 0 lúc truyền vào console.log().

Để khiến cho tác vụ console.log(square * n) chờ tác vụ square = squareOf(n), ta cần sử dụng từ khóa await như sau:

async function cubeOf(n) {
  const promise = new Promise((resolve) => {
    setTimeout(() => {
      resolve(squareOf(n))
    }, 3000)
  })
 
  let square = await promise // <-- blocking here
 
  console.log(square * n)
}

await chỉ được dùng cho một promise, ta cần tạo ra một promise và đem tất cả các tác vụ bất đồng bộ vào đó. Đồng thời, await chỉ có thể được sử dụng bên trong một hàm async, ta cần phải thêm từ khóa async vào trước hàm.

Từ khóa await ở đoạn code trên có hai chức năng:

  • Thứ nhất, await khiến cho tác vụ console.log(square * n) đến sau sẽ phải chờ cho promise hoàn thành công việc của nó.
  • Thứ hai, await trích xuất kết quả trả về và gán cho biến square.

Trong trường hợp tác vụ bất đồng bộ xảy ra lỗi, ta cần dùng hai từ khóa trycatch để xử lý lỗi:

async function cubeOf(n) {
  try {
    const promise = new Promise((resolve) => {
      setTimeout(() => {
        resolve(squareOf(n))
      }, 3000)
    })
 
    let square = await promise
 
    console.log(square * n)
  } catch (e) {
    console.log(e)
  }
}

Ngoài ra, ta cũng có thể dùng thêm từ khóa finally sau khối lệnh của từ khóa catch.

Using async/await with Fetch API

Lấy dữ liệu từ API bằng hàm Fetch API và xử lý bằng async/await:

const url = "https://nekos.best/api/v2/neko"
 
async function fetchData() {
  try {
    const response = await fetch(url) // <-- wait for a promise
    const data = await response.json() // <-- wait for a promise
    return data
  } catch (err) {
    console.error(err)
  }
}
 
async function displayData() {
  const data = await fetchData() // <-- wait for a promise
  console.log(data)
}
 
displayData()