Lời hứa và không đồng bộ đang chờ trong JavaScript là gì?

Lời hứa là một trong những bổ sung tốt nhất cho JavaScript vì chúng giúp xử lý mã không đồng bộ dễ dàng hơn rất nhiều. Chuyển từ gọi lại sang lời hứa giống như một bản nâng cấp lớn, nhưng có một thứ thậm chí còn tốt hơn lời hứa và đó là không đồng bộ/chờ đợi. Async/await là một cú pháp thay thế cho những lời hứa làm cho việc đọc/ghi mã async trở nên dễ dàng hơn, nhưng có một số lưu ý bạn cần biết về async/await nếu không bạn có thể làm cho mã của mình trở nên tồi tệ hơn

Nếu bạn không quen với những lời hứa, hãy đọc hướng dẫn đầy đủ về lời hứa này trước

Nếu bạn muốn học trực quan, hãy xem phiên bản video của bài viết này

Thông tin cơ bản về Async/Await

Để hiểu async/await, cách dễ nhất là bắt đầu với một ví dụ về lời hứa đang được sử dụng và sau đó chuyển đổi nó thành async/await. Để bắt đầu, chúng tôi sẽ sử dụng chức năng dưới đây trong tất cả các ví dụ của chúng tôi

function setTimeoutPromise(delay) {
  return new Promise((resolve, reject) => {
    if (delay < 0) return reject("Delay must be greater than 0")

    setTimeout(() => {
      resolve(`You waited ${delay} milliseconds`)
    }, delay)
  })
}

Chức năng này chỉ đơn giản là một phiên bản dựa trên lời hứa của setTimeout. Bây giờ hãy xem cách chúng ta xâu chuỗi hai thời gian chờ lại với nhau trong đó thời gian chờ thứ hai chờ thời gian chờ thứ nhất kết thúc

setTimeoutPromise(250).then(msg => {
  console.log(msg)
  console.log("First Timeout")
  return setTimeoutPromise(500)
}).then(msg => {
  console.log(msg)
  console.log("Second Timeout")
})
// Output:
// You waited 250 milliseconds
// First Timeout
// You waited 500 milliseconds
// Second Timeout

Nếu bạn đã quen với những lời hứa thì mã này không quá khó hiểu. Phần khó hiểu nhất của mã là chúng tôi đang trả lại lời hứa thứ hai từ lời hứa đầu tiên để chúng tôi có thể xâu chuỗi chúng lại với nhau. Bây giờ mã này hoạt động tốt, nhưng chúng tôi có thể làm cho mã sạch hơn rất nhiều với async/await

doStuff()
async function doStuff() {
  const msg1 = await setTimeoutPromise(250)
  console.log(msg1)
  console.log("First Timeout")

  const msg2 = await setTimeoutPromise(500)
  console.log(msg2)
  console.log("Second Timeout")
}
// Output:
// You waited 250 milliseconds
// First Timeout
// You waited 500 milliseconds
// Second Timeout

Đoạn mã trên thực hiện chính xác như phiên bản trước, nhưng bạn sẽ nhận thấy nó trông giống mã đồng bộ thông thường hơn, đó là điểm của async/await. Bây giờ hãy nói về cách mã này hoạt động

Trước tiên, bạn sẽ nhận thấy rằng chúng tôi đã gói tất cả mã của mình trong một hàm có tên là

setTimeoutPromise(250).then(msg => {
  console.log(msg)
  console.log("First Timeout")
  return setTimeoutPromise(500)
}).then(msg => {
  console.log(msg)
  console.log("Second Timeout")
})
// Output:
// You waited 250 milliseconds
// First Timeout
// You waited 500 milliseconds
// Second Timeout
0. Tên của chức năng này không quan trọng, nhưng bạn sẽ nhận thấy rằng chúng tôi đã gắn nhãn chức năng này là không đồng bộ bằng cách đặt từ khóa async trước từ khóa chức năng. Làm điều này cho JavaScript biết rằng chúng tôi dự định sử dụng từ khóa chờ đợi trong chức năng này. Nếu chúng ta không gắn nhãn chức năng là không đồng bộ và sử dụng từ khóa đang chờ trong chức năng, nó sẽ báo lỗi

Cũng cần lưu ý rằng hiện tại bạn không thể sử dụng từ khóa chờ đợi trừ khi bạn đang ở trong một hàm, đó là lý do tại sao chúng tôi phải tạo một hàm để chạy mã này. Đây là điều mà JavaScript đang có kế hoạch thay đổi bằng cách thêm tính năng chờ ở cấp cao nhất, điều đó có nghĩa là bạn có thể sử dụng tính năng chờ ở cấp cao nhất của tệp mà không cần ở trong một chức năng, nhưng tính năng này vẫn chưa có trong bất kỳ trình duyệt nào

Bây giờ chúng ta đã hiểu từ khóa async, hãy nói về mã trong hàm. Bạn sẽ nhận thấy nó trông rất giống với mã trước đó và đó là bởi vì async/await chỉ là một cách khác để viết cùng một thứ. Để chuyển đổi một lời hứa. sau đó để không đồng bộ/chờ đợi, bạn cần gọi hàm hứa hẹn

setTimeoutPromise(250).then(msg => {
  console.log(msg)
  console.log("First Timeout")
  return setTimeoutPromise(500)
}).then(msg => {
  console.log(msg)
  console.log("Second Timeout")
})
// Output:
// You waited 250 milliseconds
// First Timeout
// You waited 500 milliseconds
// Second Timeout
1 và đặt từ khóa
setTimeoutPromise(250).then(msg => {
  console.log(msg)
  console.log("First Timeout")
  return setTimeoutPromise(500)
}).then(msg => {
  console.log(msg)
  console.log("Second Timeout")
})
// Output:
// You waited 250 milliseconds
// First Timeout
// You waited 500 milliseconds
// Second Timeout
2 trước nó. Điều này cho JavaScript biết rằng chức năng sau từ khóa chờ đợi không đồng bộ. Sau đó, nếu lời hứa của bạn trả về một giá trị, bạn chỉ cần truy cập giá trị đó như thể đó là một hàm trả về bình thường như chúng ta đã làm với
setTimeoutPromise(250).then(msg => {
  console.log(msg)
  console.log("First Timeout")
  return setTimeoutPromise(500)
}).then(msg => {
  console.log(msg)
  console.log("Second Timeout")
})
// Output:
// You waited 250 milliseconds
// First Timeout
// You waited 500 milliseconds
// Second Timeout
3. Bước cuối cùng là lấy tất cả mã từ. sau đó là một phần của lời hứa và đặt nó sau lệnh gọi hàm

Lý do điều này hoạt động là vì khi JavaScript nhìn thấy từ khóa đang chờ, nó sẽ gọi hàm được chờ, nhưng nó sẽ không chạy bất kỳ mã nào sau hàm đó cho đến khi lời hứa được trả về bởi hàm đó được giải quyết. Thay vào đó, JavaScript sẽ chạy mã ở những nơi khác trong ứng dụng của bạn trong khi đợi lời hứa giải quyết. Khi lời hứa được giải quyết, nó sẽ trả về kết quả lời hứa từ chức năng được chờ đợi và chạy tất cả mã cho đến câu lệnh chờ đợi tiếp theo và lặp lại

bắt lỗi

Phần trên bao gồm những điều cơ bản tuyệt đối về async/await, nhưng điều gì sẽ xảy ra nếu lời hứa của bạn từ chối thay vì giải quyết. Điều này rất dễ nắm bắt với cú pháp lời hứa truyền thống

setTimeoutPromise(-10).then(msg => {
  console.log(msg)
}).catch(error => {
  console.error(error)
})
// Output:
// Delay must be greater than 0

Với async/await, điều này phức tạp hơn một chút

________số 8_______

Để bắt lỗi, bạn cần bọc mã của mình trong khối thử/bắt. Tất cả mã có thể bị lỗi (chẳng hạn như lời hứa) cần được đưa vào phần thử của khối thử/bắt. JavaScript sẽ cố chạy mã này và tại bất kỳ thời điểm nào nếu có lỗi, nó sẽ ngừng chạy mã trong khối thử và chuyển sang khối bắt

Khối bắt mã nhận một tham số lỗi. Nếu có lỗi trong chương trình, mã này sẽ chạy và sau đó chương trình sẽ tiếp tục sau khối try/catch như bình thường. Nếu không có lỗi, mã sẽ chạy qua toàn bộ khối thử, bỏ qua khối bắt và tiếp tục như bình thường

Bạn cũng có thể xử lý phần cuối cùng của lời hứa theo cách tương tự

setTimeoutPromise(-10).then(msg => {
  console.log(msg)
}).catch(error => {
  console.error(error)
}).finally(() => {
  console.log("Runs no matter what")
})
// Output:
// Delay must be greater than 0
// Runs no matter what

doStuff()
async function doStuff() {
  try {
    const msg = await setTimeoutPromise(-10)
    console.log(msg)
  } catch (error) {
    console.error(error)
  } finally {
    console.log("Runs no matter what")
  }
}
// Output:
// Delay must be greater than 0
// Runs no matter what

Cảnh báo không đồng bộ/đang chờ

Async/await thật đáng kinh ngạc khi xử lý mã không đồng bộ, nhưng nếu bạn cần xử lý mã không đồng bộ chạy song song thì nó không hoạt động

Hãy tưởng tượng một kịch bản trong đó bạn đang lặp qua một tập hợp các giá trị và muốn làm điều gì đó với các giá trị không đồng bộ này

for (let i = 0; i < 10; i++) {
  getUser(i).then(user => console.log(user))
}

Mã này sẽ tạo 10 lời hứa chạy trong nền cùng một lúc và sẽ đăng xuất tất cả 10 người dùng gần như cùng một lúc nếu hàm

setTimeoutPromise(250).then(msg => {
  console.log(msg)
  console.log("First Timeout")
  return setTimeoutPromise(500)
}).then(msg => {
  console.log(msg)
  console.log("Second Timeout")
})
// Output:
// You waited 250 milliseconds
// First Timeout
// You waited 500 milliseconds
// Second Timeout
4 mất cùng thời gian để chạy mỗi khi nó được gọi

await function doStuff() {
  for (let i = 0; i < 10; i++) {
    const user = await getUser(i)
    console.log(user)
  }
}

Bạn có thể nghĩ rằng đoạn mã trên làm điều tương tự, nhưng điều này thực sự sẽ chạy lần lượt từng hàm

setTimeoutPromise(250).then(msg => {
  console.log(msg)
  console.log("First Timeout")
  return setTimeoutPromise(500)
}).then(msg => {
  console.log(msg)
  console.log("Second Timeout")
})
// Output:
// You waited 250 milliseconds
// First Timeout
// You waited 500 milliseconds
// Second Timeout
4. Lý do cho điều này là vì trong lần lặp đầu tiên của vòng lặp, chúng tôi gọi
setTimeoutPromise(250).then(msg => {
  console.log(msg)
  console.log("First Timeout")
  return setTimeoutPromise(500)
}).then(msg => {
  console.log(msg)
  console.log("Second Timeout")
})
// Output:
// You waited 250 milliseconds
// First Timeout
// You waited 500 milliseconds
// Second Timeout
4 và đợi nó nhận người dùng trước khi tiếp tục. Sau khi nhận được người dùng, chúng tôi đăng xuất và sau đó đợi để nhận người dùng tiếp theo. Điều này lặp lại cho tất cả người dùng

với. sau đó, phiên bản dựa trên lời hứa, chúng tôi không bao giờ đợi người dùng tiếp tục, điều đó có nghĩa là chúng tôi thực hiện toàn bộ vòng lặp gọi

setTimeoutPromise(250).then(msg => {
  console.log(msg)
  console.log("First Timeout")
  return setTimeoutPromise(500)
}).then(msg => {
  console.log(msg)
  console.log("Second Timeout")
})
// Output:
// You waited 250 milliseconds
// First Timeout
// You waited 500 milliseconds
// Second Timeout
4 mà không cần chờ đợi. Điều này có nghĩa là nếu hàm
setTimeoutPromise(250).then(msg => {
  console.log(msg)
  console.log("First Timeout")
  return setTimeoutPromise(500)
}).then(msg => {
  console.log(msg)
  console.log("Second Timeout")
})
// Output:
// You waited 250 milliseconds
// First Timeout
// You waited 500 milliseconds
// Second Timeout
4 mất 50 mili giây để chạy, ví dụ đầu tiên sẽ gọi tất cả các hàm
setTimeoutPromise(250).then(msg => {
  console.log(msg)
  console.log("First Timeout")
  return setTimeoutPromise(500)
}).then(msg => {
  console.log(msg)
  console.log("Second Timeout")
})
// Output:
// You waited 250 milliseconds
// First Timeout
// You waited 500 milliseconds
// Second Timeout
4 và sau đó 50 mili giây, nó sẽ in ra tất cả người dùng. Ví dụ thứ hai sẽ gọi hàm
setTimeoutPromise(250).then(msg => {
  console.log(msg)
  console.log("First Timeout")
  return setTimeoutPromise(500)
}).then(msg => {
  console.log(msg)
  console.log("Second Timeout")
})
// Output:
// You waited 250 milliseconds
// First Timeout
// You waited 500 milliseconds
// Second Timeout
4 đầu tiên, đợi 50 mili giây, in người dùng ra, sau đó gọi hàm
setTimeoutPromise(250).then(msg => {
  console.log(msg)
  console.log("First Timeout")
  return setTimeoutPromise(500)
}).then(msg => {
  console.log(msg)
  console.log("Second Timeout")
})
// Output:
// You waited 250 milliseconds
// First Timeout
// You waited 500 milliseconds
// Second Timeout
4 lần thứ hai trước khi đợi 50 mili giây và lặp lại. Điều này có nghĩa là phiên bản async/await sẽ mất 500 mili giây để chạy qua tất cả người dùng

Vì điều này, tôi khuyên bạn không bao giờ nên sử dụng async/await trong một vòng lặp trừ khi bạn đặc biệt cần đợi mỗi lần lặp trước đó của vòng lặp trước khi có thể hoàn thành lần lặp tiếp theo

Phần kết luận

Mặc dù async/await không cho phép bạn làm bất cứ điều gì mới trong JavaScript nhưng nó vẫn cực kỳ hữu ích vì nó có thể làm cho mã bạn viết sạch hơn bao nhiêu

Lời hứa trong JavaScript là gì?

Lời hứa là đại diện cho một giá trị không nhất thiết phải biết khi lời hứa được tạo . Nó cho phép bạn liên kết các trình xử lý với giá trị thành công cuối cùng hoặc lý do thất bại của hành động không đồng bộ.

Tại sao sử dụng async đang chờ so với lời hứa?

Không đồng bộ/Đang chờ so với lời hứa . Không đồng bộ và chờ đợi cho phép bạn bọc mã của mình bằng cách sử dụng thử và bắt để xử lý lỗi. Điều này có thể giúp quản lý lỗi dễ dàng hơn so với chuỗi lời hứa bị từ chối và bắt()using async and await results in more concise and easier to reason about code. Async and await allows you to wrap your code using a try and catch to handle errors. This can make it easier to manage errors compared to promise rejected chaining and catch()

Lời hứa và chờ đợi là gì?

await thường được sử dụng để mở lời hứa bằng cách chuyển một Lời hứa dưới dạng biểu thức . Việc sử dụng await sẽ tạm dừng việc thực thi chức năng không đồng bộ xung quanh của nó cho đến khi lời hứa được giải quyết (nghĩa là được thực hiện hoặc bị từ chối). Khi thực thi tiếp tục, giá trị của biểu thức chờ đợi trở thành giá trị của lời hứa đã thực hiện.

Sự khác biệt giữa không đồng bộ và chờ đợi trong JavaScript là gì?

cú pháp. // chỉ hoạt động bên trong các hàm async let value = await promise;