Lâu rồi chưa viết bài về kĩ thuật nên hôm nay viết một bài để dân tình biết là mình vẫn còn code nhé!
Kì này, chúng ta sẽ tìm hiểu về async/await, một cặp từ khoá rất bá đạo trong JavaScript chuẩn ES2017. Biết cách dùng async/await sẽ giúp ta viết code ngắn gọn, hiệu quả và dễ dàng hơn nhiều nhé.
Nhắc lại kiến thức
JavaScript là ngôn ngữ single-thread, tức là chỉ có một thread duy nhất để thực thi các dòng lệnh. Nếu chạy theo cơ chế đồng bộ (synchonous) thì khi thực hiện tính toán phức tạp, gọi AJAX request tới server, gọi database (trong NodeJS), thread này sẽ dừng để chờ, làm toàn bộ trình duyệt bị… treo.
Để tránh điều này, hầu hết code gọi AJAX request hoặc database trong JavaScript đều chạy theo cơ chế bất đồng bộ (asynchnous). Ban đầu, việc chạy code asynchnous trong JavaScript được hiện thực nhờ callback (như đoạn code bên dưới).
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Truyền callback vào hàm ajax | |
var callback = function(image){ | |
console.log(image); | |
}; | |
ajax.get("gaixinh.info", callback); | |
// Có thể viết gọn như sau | |
ajax.get("gaixinh.info", function(image) { | |
console.log(image); | |
}) |
Tất nhiên, vì callback có một số nhược điểm như code dài dòng, callback hell,… nên người ta tạo ra 1 pattern mới gọi là Promise!
Các bạn nên xem lại kiến thức về Callback trong JavaScript và Promise trong JavaScript để có thể hiểu kiến thức phía dưới bài này nhé!
Từ callback, promise đến Async/Await
Promise đã giải quyết khá tốt những vấn đề của callback. Code trở nên dễ đọc, tách biệt và dễ bắt lỗi hơn.

Tuy nhiên, dùng promise đôi khi ta vẫn thấy hơi khó chịu vì phải truyền callback vào hàm then và catch. Code cũng sẽ hơi dư thừa và khó debug, vì toàn bộ các hàm then chỉ được tính là 1 câu lệnh nên không debug riêng từng dòng được.
May thay, trong ES7 một phép màu mang tên async/await đã ra đời. (Mình nghi 99% là phép màu này ăn cắp từ C# hay ho, vì C# đã có async/await từ thời ông địa cởi trường rồi cơ).

Vậy async/await có gì hay ho? Chúng giúp chúng ta viết code trông có vẻ đồng bộ (synchonous), nhưng thật ra lại chạy bất đồng bộ (asynchonous).
Như trong hình phía trên, hàm findRandomImgPromise là hàm bất đồng bộ, trả về một Promise. Với từ khoá await, ta có thể coi hàm này là đồng bộ, câu lệnh phía sau chỉ được chạy sau khi hàm này trả về kết quả.
Bấm Run Pen để xem demo nhé!
Tại sao nên dùng async/await?
Như mình đã nói, async/await có một số ưu điểm vượt trội so với promise:
- Code dễ đọc hơn rất rất nhiều, không cần phải then rồi catch gì cả, chỉ viết như code chạy tuần tự, sau đó dùng try/catch thông thường để bắt lỗi.
- Viết vòng lặp qua từng phần tử trở nên vô cùng đơn giản, chỉ việc await trong mỗi vòng lặp.
- 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 cực hình vì ta phải viết code lòng vòng, rắc rối. Với async/await, việc này vô cùng dễ dàng.

Một demo loop khá cool bằng async await
Bất cập của async/await
Tất nhiên, async/await cũng có một số bất cập mà các bạn cần phải lưu ý khi sử dụng:
- Không chạy được trên các trình duyệt cũ. Nếu dự án yêu cầu phải chạy trên các trình duyệt cũ, bạn sẽ phải dùng Babel để transpiler code ra ES5 để chạy.
- Khi ta await một promise bị reject, JavaScript sẽ throw một Exception. Do đó, nếu dùng async await mà quên try catch thì lâu lâu chúng ta sẽ bị… nuốt lỗi hoặc code ngừng chạy.
- async và await bắt buộc phải đi kèm với nhau! await chỉ dùng được trong hàm async, nếu không sẽ bị syntax error. Do đó, async/await sẽ lan dần ra toàn bộ các hàm trong code của bạn.

Áp dụng async/await vào code
Về bản chất, một hàm async sẽ trả ra một promise, tương ứng với Task trong C#. Do vậy, để có thể dùng async/await một cách hiệu quả, các bạn phải nắm rõ cơ chế làm việc của Promise nhé!
Hiện tại các phiên bản mới nhất của Chrome, Edge và Firefox đã hỗ trợ async/await, nếu dự án không bắt hỗ trợ các trình duyệt cũ thì các bạn cứ thoải mái dùng async/await để code gọn đẹp hơn nhé.
Ngoài ra, nếu các bạn sử dụng NodeJS, có thể sử dụng combo Promisify + Async/Await như sau:
- Sử dụng Bluebird hoặc util.promisify (Node 8 trở lên) để biến các hàm callback của NodeJS thành Promise.
- Dùng async/await để lấy kết quả từ các Promise này.
Kết
Bài viết kì này khá phức tạp, lại cần nhiều kiến thức nền về JavaScript nên sẽ hơi khó hiểu.
Các bạn ráng đọc lại 2,3 lần, xem lại các code mẫu để hiểu nha. Nếu có thắc mắc hay có gì cần chia sẻ, các bạn cứ thoải mái comment nhé!
Bonus
Slide và clip thuyết trình của mình tại SingaporeJS
Link tham khảo thêm
Giọng của thanh niên lạ tai phết nhể =))
Mà ko thể đỡ nổi giọng Singlish của các bạn Singapore. Bập bùng như tiếng đàn tơ rưng.
LikeLike
Ve VN lam giao vien duoc do e, bo^n ba chi cho xa xoi vay 🙂
Giao vien day code gio giau lam B-)
LikeLike
cho em hỏi trong C# thì async/await nó có khác gì với BackgroundWorker không a. Anh có thể làm bài giới thiệu về chúng không ạ, anh có bài viết nào về async/await không ạ
LikeLike
Sau này rảnh a mới viết e nhé 😉
LikeLike
Vâng, cảm ơn những bài viết rất hay và bá đạo của anh 😀
LikeLike
Một nhược điểm nữa của async/await là async/await chỉ có thể chạy tuần tự chứ không thể chạy song song nhiều asynchronous request như Promise.all
LikeLike
Bậy, dùng Promise.all để gom nhiều promise thành 1 promise rồi await cái promise đó đc nhé 😉
LikeLike
async/await chạy song song nhiều async request được mà bạn.
https://jsfiddle.net/yustar/pun1mppu/
LikeLike
Mình có 1 thắc mắc là
Dùng async/await có bị block event loop không nhỉ?
Vì các dòng code dưới dòng dùng await phải đợi await xong?
LikeLike
a Hoàng ơi a có thể cho e hỏi là e học căn bản js rồi. muốn làm 1 cái gì đó chỉ cần search google là ra ngay những đoạn code sẵn. nhìn cũng hiểu nguyên lí hoạt động của nó. nhưng dùng luôn tiện hơn rất nhiều tự ngồi tư duy code từ a-z. không biết khi đi làm thực tế nhà tuyển dụng có yêu cầu mình phải tự code ko. hay mình lấy đâu cũng đc miễn là có sản phẩm là ok hả a 😀
LikeLike
một số ng phỏng vấn sẽ yêu cầu tự code, hoặc đơn giản hơn là cho bạn xem đoạn code rồi nói kết quả. Gì chứ JS dùng scope, this, callback, promise để test dạng trắc nghiệm hack não thì thấy everywhere luôn.
Bạn nói hầu hết bạn copy được từ trên mạng xuống và dùng, mình nghĩ những bài tập bạn học nó quá phổ biến và có sẵn ng giải thích trên mạng rồi, chứ khi đi làm sẽ gặp nhiều thứ ko có sẵn đâu
LikeLike