Mọi ngôn ngữ lập trình đều có những ưu và nhược điểm riêng. Javascript – một trong những ngôn ngữ lập trình phổ biến nhất hiện nay, đã trải qua một khoảng thời gian dài khó khăn khi phải dựa vào callbacks để lập trình các đoạn code bất đồng bộ. Sau đó, trong bản cập nhật ES7, Javascript đã đưa ra một giải pháp mới được cộng đồng lập trình rất ủng hộ để giải quyết vấn đề bất đồng bộ chính là Async/Await.
Table of Contents
Async/Await là gì?
Trước đây, để làm việc với code bất đồng bộ, chúng ta sử dụng callback và promise. Async/Await là một cách mới để viết code bất đồng bộ, được Javascript giới thiệu từ bản cập nhật ES7. 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. Trong đó:
-
Async được dùng để khai báo một hàm bất đồng bộ. Các hàm bất đồng bộ sẽ luôn trả về một giá trị. Việc sử dụng async chỉ đơn giản là ngụ ý rằng một lời hứa sẽ được trả lại và nếu một lời hứa không được trả lại, Javascript sẽ tự động kết thúc nó.
-
Await được sử dụng để chờ một Promise. Nó chỉ có thể được sử dụng bên trong một khối Async. Từ khóa Await làm cho Javascript đợi cho đến khi promise trả về kết quả. Cần lưu ý rằng nó chỉ làm cho khối chức năng không đồng bộ chờ đợi chứ không phải toàn bộ chương trình thực thi.
Bạn đang đọc: Async/Await Trong Javascript Có Làm Khó Được Bạn?
Cú pháp Async/Await
1. Async
Từ khóa Async
được đặt trước một hàm làm cho hàm trả về Promise.
Ví dụ:
async function myFunction() {
return "Hello";
}
Tương tự như trên:
async function myFunction() {
return Promise.resolve("Hello");
}
Dưới đây là cách sử dụng Promise:
myFunction().then(
function(value) { /* code if successful */ },
function(error) { /* code if some error */ }
);
2. Await
Từ khóa Await
được đặt trước một hàm làm cho hàm chờ một Promise.
let value = await promise;
Từ khóa await
chỉ có thể được sử dụng bên trong một hàm không đồng bộ.
Ví dụ:
<!DOCTYPE html>
<html>
<body>
<h2>JavaScript async / await</h2>
<h1 id="demo"></h1>
<script>
async function myDisplay() {
let myPromise = new Promise(function(myResolve, myReject) {
myResolve("I love You !!");
});
document.getElementById("demo").innerHTML = await myPromise;
}
myDisplay();
</script>
</body>
</html>
Output:
Những điều cần chú ý quan tâm khi sử dụng Async/Await
1. Chúng ta không hề sử dụng Await bên trong những hàm thông thường
Ví dụ:
function firstAsync() {
let promise = Promise.resolve(10);
let result = await promise; // Syntax error
}
Để hàm trên hoạt động bình thường, chúng ta cần thêm từ khóa async
trước function firstAsync();
2. Async/Await thực hiện tuần tự
Đây không hẳn là điều xấu, nhưng triển khai song song sẽ nhanh hơn nhiều.
Ví dụ:
async function sequence() {
await promise1(50); // Wait 50ms…
await promise2(50); // …then wait another 50ms.
return "done!";
}
Đoạn code trên mất 100ms để triển khai xong, không phải một khoảng thời gian lớn nhưng vẫn chậm. Điều này xảy ra là do nó đang diễn ra theo trình tự. Cả hai hàm đều mất 50ms để triển khai xong. Hàm thứ hai chỉ triển khai sau khi hàm tiên phong được xử lý. Điều này không phải là một thực tiễn tốt, vì những tác vụ lớn hoàn toàn có thể tốn nhiều thời gian. Chúng ta cần triển khai song song.
Chúng ta có thể làm điều đó bằng cách sử dụng Promise.all()
Theo MDN: “Phương thức Promise.all(iterable)
trả ra một Promise mới và promise mới này chỉ được kết thúc khi tất cả các promise trong iterable
kết thúc hoặc có một promise nào đó xử lý thất bại. Kết quả của promise mới này là một mảng chứa kết quả của tất cả các promise theo đúng thứ tự hoặc kết quả lỗi của promise gây lỗi.”
Ví dụ:
Xem thêm: 10 trang mạng xã hội làm kinh doanh online phải biết! (P2)
async function sequence() {
await Promise.all([promise1(), promise2()]);
return "done!";
}
Hàm Promise.all()
giải quyết khi tất cả các Promise bên trong có thể được giải quyết và sau đó trả về kết quả.
Xử lí lỗi với Async/Await
Kết quả bình thường khi bạn sử dụng async/await là một kết quả đã được resolve. Nhưng khi Promise bị reject và exception xảy ra thì phải làm gì? Chúng ta có thể dùng try...catch
để xử lí các lỗi này như các hàm thông thường khác.
Ví dụ:
Nếu một Promise giải quyết bình thường, sau đó await promise
trả về kết quả. Nhưng trong trường hợp từ chối, nó sẽ ném lỗi, giống như có một câu lệnh throw
tại dòng đó.
async function f() {
await Promise.reject(new Error("Whoops!"));
}
Trong tình huống thực tế, Promise có thể mất một thời gian trước khi nó bị từ chối. Trong trường hợp đó, sẽ có độ trễ trước khi await
đưa ra lỗi.
Chúng ta có thể bắt lỗi đó bằng cách sử dụng try...catch
, giống như cách thông thường throw
:
async function f() {
try {
let response = await fetch('http://no-such-url');
} catch(err) {
alert(err); // TypeError: failed to fetch
}
}
f();
Tại sao nên dùng Async/Await?
1. Ngắn gọn và dễ hiểu
Ưu điểm đơn giản nhất chính là số lượng code ta cần viết đã giảm đi đáng kể. Không cần phải dùng then
rồi catch
gì cả, chỉ viết code chạy tuần tự, sau đó dùng try/catch
thông thường để xử lí lỗi.
2. Xử lí lỗi
Async/Await giúp ta xử lí cả lỗi đồng bộ lẫn lỗi bất đồng bộ theo cùng một cấu trúc. Tạm biệt try/catch
. Với đoạn code dùng Promise, try/catch
sẽ không bắt được lỗi nếu JSON.parse
lỗi do nó xảy ra bên trong Promise. Ta cần gọi .catch
bên trong Promise và lặp lại code xử lí error, điều mà chắc chắn sẽ trở nên rắc rối hơn cả console.log
trong đoạn code production.
const makeRequest = () => {
try {
getJSON()
.then(result => {
// this parse may fail
const data = JSON.parse(result)
console.log(data)
})
// uncomment this block to handle asynchronous errors
// .catch((err) => {
// console.log(err)
// })
} catch (err) {
console.log(err)
}
}
Bây giờ hãy nhìn vào đoạn code sử dụng Async/Await. Khối catch
giờ sẽ xử lí các lỗi parsing.
const makeRequest = async () => {
try {
// this parse may fail
const data = JSON.parse(await getJSON())
console.log(data)
} catch (err) {
console.log(err)
}
}
3. Câu lệnh điều kiện kèm theo
Hãy tưởng tượng một cái gì đó giống như đoạn mã bên dưới tìm nạp một số tài liệu và quyết định hành động xem nó có nên trả lại tài liệu đó hay không hoặc lấy thêm cụ thể dựa trên một số giá trị trong tài liệu.
const makeRequest = () => {
return getJSON()
.then(data => {
if (data.needsAnotherRequest) {
return makeAnotherRequest(data)
.then(moreData => {
console.log(moreData)
return moreData
})
} else {
console.log(data)
return data
}
})
}
Chỉ nhìn vào đoạn code trên thôi cũng đã khiến bạn đau đầu. Thật không thuận tiện bị lạc trong toàn bộ những câu lệnh lồng nhau, dấu ngoặc nhọn và trả về chỉ thiết yếu để truyền tải tác dụng sau cuối cho đến hàm chính.
Khi sử dụng Async/Await, ta sẽ có đoạn code mới dễ đọc hơn.
const makeRequest = async () => {
const data = await getJSON()
if (data.needsAnotherRequest) {
const moreData = await makeAnotherRequest(data);
console.log(moreData)
return moreData
} else {
console.log(data)
return data
}
}
Với Promise hoặc callback, việc tích hợp if/else hoặc retry với code async 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 thuận tiện.
4. Debug
Cuối cùng, khi bạn thao tác với Async/Await, việc debug trở nên rất đơn giản. Với Async/Await, bạn không cần nhiều arrow function và bạn hoàn toàn có thể thực thi những cuộc gọi chờ đúng mực như thể chúng là những cuộc gọi đồng nhất thông thường.
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.
Xem thêm: Xu hướng trải nghiệm người dùng 2021 giúp bạn phát triển kinh doanh
Tạm kết
Như vậy, trong bài viết này chúng ta đã khám phá những yếu tố cơ bản về Async/Await trong Javascript. Nếu bạn thấy bài viết hữu ích, hãy đánh giá 5 sao và chia sẻ cho mọi người tìm hiểu thêm!
Hãy để lại comment để mình hoàn toàn có thể triển khai bản thân hơn trong tương lai. Cám ơn bạn!
Nguồn tìm hiểu thêm: https://wikifin.net