- vừa được xem lúc

Blog#36: Mẹo đơn giản để cải thiện hiệu suất với Async/Await 😊 (Series: Bí kíp Javascript - PHẦN 31)

0 0 19

Người đăng: NGUYỄN ANH TUẤN

Theo Viblo Asia

Mình là TUẤN hiện đang là một Full-stack Developer tại Tokyo 😉. Nếu bạn thấy Blog này hay xin hãy cho mình một like và đăng ký để ủng hộ mình nhé 😊.

Bạn có phải là người thích sử dụng cú pháp async/await không? Mình cũng vậy, theo quan điểm của mình thì async/await tốt hơn là Promise chains. Nhưng mình tự hỏi liệu bạn đã sử dụng nó đúng cách chưa. Đôi khi nó có thể làm cho chương trình của bạn chạy chậm hơn mong đợi. Trong bài viết này, mình sẽ chia sẻ một điều rất quan trọng nếu bạn muốn cải thiện hiệu suất khi sử dụng async/ await và điều đó cực kỳ dễ áp ​​dụng trong project của bạn.

Before Reading

Nhiều JavaScript Dev yêu thích cú pháp async/ await. Mình nghĩ lý do mà tại sao mọi người thích cú pháp này là vì Promise hell, nó tiếp tục tạo ra một chain khi nhiền lần chấm then liên tiếp.

async/ await cho phép các bạn giảm bớt hoặc thoát khỏi Promise hell bằng cách sử dụng keyword await. Nhưng bạn đã bao giờ nghĩ rằng cú pháp này có thể làm chậm ứng dụng của bạn khi nó được sử dụng theo cách không tối ưu chưa?

Vậy chúng ta phải làm gì? Hãy nói về các lựa chọn của chúng ta nào.

Helper Functions

Giả sử có một hàm được gọi là sleep. Nó sẽ chờ một khoảng thời gian bạn muốn và là một Promise cơ bản.

const sleep = t => { return new Promise (res => { setTimeout (res, t); }); };

Và chúng ta cũng có một hàm fetch

const fetch = url => { const time = url.length \* 1000; ngủ trở lại (thời gian); };

Hàm giả fetch nhận đối số kiểu string và nó được sử dụng để tính toán thời gian chờ. Nếu chúng ta gọi fetch('/notice'), thời gian chờ sẽ là bảy giây vì /notice có bảy ký tự.

Tình huống cơ bản và vấn đề gặp phải của ví dụ này là

Bây giờ, chúng ta truy cập ứng dụng; nó có lẽ là /. Ngay sau khi chúng ta truy cập trang chính, code sẽ cố gắng load dữ liệu bằng cách nhập fetch. (Ở ví dụ này mình dùng React nhé)

// React base
async componentDidMount() { const banners = await fetch('/banners'); const events = await fetch('/events'); const notices = await fetch('/notices'); /* Do other tasks here */ this.setState({ ...this.state, banners, events, notices });
}

Chúng ta đã thực hiện một hàm fetch trước đó, đó là một Promise. Nó có đối số là 1 string.

  • /banners → tám ký tự/độ trễ tám giây
  • /events → bảy ký tự/độ trễ bảy giây
  • /notices → tám ký tự/độ trễ tám giây

Biểu đồ hiển thị thông tin ở trên:/biểu ngữ → 8 ký tự/độ trễ 8 giây/sự kiện → 7 ký tự/độ trễ 7 giây, v.v.

Tổng thời gian fetch sẽ là 23 giây vì nó đợi quá trình fetch hiện tại kết thúc trước khi gọi lần tiếp theo.

Cấu trúc này ổn nếu thứ tự fetch là quan trọng. Ví dụ: chúng ta nên fetch /logintrước /my-profile vì đó là thông tin chỉ dành cho thành viên.

Điều gì sẽ xảy ra nếu có nhiều hơn năm API mà bạn cần fetch trước khi hiển thị? Càng nhiều API, user của bạn sẽ đợi lâu hơn trước khi xem trang.

Giải phảp

Giải pháp rất rõ ràng và đơn giản.

// React base
async componentDidMount() { const bannersFetch = fetch('/banners'); const eventsFetch = fetch('/events'); const noticesFetch = fetch('/notices'); /* Do other tasks here */ const banners = await bannersFetch; const events = await eventsFetch; const notices = await noticesFetch; this.setState({ ...this.state, banners, events, notices });
}

Điểm khác biệt duy nhất so với phiên bản trước đó là chúng ta không còn đặt keyword await trước fetch. Thay vào đó, chúng ta đặt nó sau. Điều gì xảy ra sau đó?

Mỗi request fetch sẽ được gửi đến server vì không ai trong số chúng chờ response trước khi kích hoạt lần fetch tiếp theo.

Mỗi fetchrequest sẽ được gửi đến server vì không có request nào trong số chúng chờ response trước khi kích hoạt request tiếp theo fetch.

Mặc dù không có thêm công việc nào trong componentDidMount ở ví dụ trên, thời gian chờ tối đa lâu nhất sẽ chỉ là tám giây.

Promise mang đến điều kỳ diệu

Lý do mà chúng ta có thể nhận được lợi ích to lớn từ việc thay đổi một vài dòng code là vì Promise. Về cơ bản, Promise được biết đến như một API hoạt động asynchronous. Tuy nhiên, bí mật thực sự được giấu trong hàng đợi sự kiện.

So sánh hàng đợi sự kiện Promise với hàng đợi sự kiện bình thường. Hàng đợi sự kiện Promise sử dụng các lệnh fetch.

JavaScript là đơn luồng (single-threaded), như nhiều người trong số các bạn đã biết. Điều đó có nghĩa là chỉ có một nhiệm vụ chạy trong chương trình trong cùng một thời điểm 😄. Vùng chứa, là một loại hàng đợi giữ các tác vụ cho đến khi đến lượt chúng được thực thi, được gọi là event queue hoặc task queue điều này có thể khác nhau tùy thuộc vào tài liệu bạn đang đọc.

Ví dụ về các nhiệm vụ có thể được coi là một normal taskconsole.log(1), obj.foo() hoặc bất cứ điều gì tương tự như vậy không phải là một job asynchronous.

Promise là một API asynchronous. Trong thời gian chạy, JavaScript gửi một tác vụ Promise (hoặc một tác vụ dựa trên Promise) đến hàng đợi sự kiện Promise. Và các nhiệm vụ trong hàng đợi Promise cũng phải đợi đến lượt được gọi. Nhưng lưu ý rằng bất kỳ tác vụ nào trong hàng đợi Promise chỉ có thể được kéo ra và chạy nếu tác vụ bình thường normal task hoàn toàn trống.

Ví dụ về ba hàng đợi promise: một cho/biểu ngữ, một cho/sự kiện và một cho/thông báo.

const banners = await fetch('/banners');
const events = await fetch('/events');
const notices = await fetch('/notices');

Do có keyword await trên mỗi dòng, hàng đợi Promise không thể nhận thêm nhiệm vụ. Thứ nhất, fetch('/banners') được thực thi. Chương trình đợi response của nó và đưa tác vụ fetch tiếp theo vào hàng đợi.

const bannersFetch = fetch('/banner');
const eventsFetch = fetch('/events');
const noticesFetch = fetch('/notices'); const banners = await bannersFetch;
const events = await eventsFetch;
const notices = await noticesFetch;

Với code này, tất cả ba fetch có thể được xem như là xếp chồng lên nhau trong hàng đợi Promise theo thứ tự từ trên xuống dưới. Những Promise đó sẽ được kéo ra và thực hiện gần như cùng một lúc (khi mà hàng đợi normal task trống), nhưng chúng không phải đợi response của fetch lần trước nữa.

Demo

<iframe src="https://codesandbox.io/embed/async-await-performance-vmo2q?fontsize=14&hidenavigation=1&theme=dark" style="width:100%; height:500px; border:0; border-radius: 4px; overflow:hidden;" title="async-await-performance" allow="accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking" sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts" ></iframe>

Một ví dụ khác

const fetchA = () => { return new Promise((callback) => { setTimeout(() => callback("OK"), 3000); });
}; const fetchB = () => { return new Promise((callback) => { setTimeout(() => callback("OK"), 2000); });
}; const fetchC = () => { return new Promise((callback) => { setTimeout(() => callback("OK"), 1000); });
}; const runFetch = async () => { const startTime = new Date().getTime(); const A = await fetchA(); const B = await fetchB(); const C = await fetchC(); const result = { A, B, C, time: (new Date().getTime() - startTime) / 1000, }; console.log("runFetch :>> ", result);
}; const runBetterFetch = async () => { const startTime = new Date().getTime(); const fetch1 = fetchA(); const fetch2 = fetchB(); const fetch3 = fetchC(); const A = await fetch1; const B = await fetch2; const C = await fetch3; const result = { A, B, C, time: (new Date().getTime() - startTime) / 1000, }; console.log("runBetterFetch :>> ", result);
}; runFetch();
runBetterFetch();

Kết quả sẽ là:

runBetterFetch :>> { A: 'OK', B: 'OK', C: 'OK', time: 3.005 }
runFetch :>> { A: 'OK', B: 'OK', C: 'OK', time: 6.03 }

Promise.all

Bạn cũng có thể sử dụng Promise.all để giải quyết vấn đề trên (hoặc Promise.allSettled trong trường hợp bạn không quan tâm đến các request không thành công).

const [banners, events, notices] = await Promise.all([ fetch("/banners"), fetch("/events"), fetch("/notices"),
]);

Kết luận

Sử dụng cú pháp async/ await làm cho cuộc sống của chúng ta dễ dàng hơn và hạnh phúc hơn, nhưng chúng ta nên sử dụng chúng tốt. Đơn giản chỉ cần đặt keyword await ở đâu đó có thể gây ra những hậu quả rất khác nhau tùy thuộc vào nơi bạn đặt nó.

Roundup

Như mọi khi, mình hy vọng bạn thích bài viết này và học thêm được điều gì đó mới.

Cảm ơn và hẹn gặp lại các bạn trong những bài viết tiếp theo! 😍

Nếu bạn thấy Blog này hay xin hãy cho mình một like và đăng ký để ủng hộ mình nhé. Thank you.😉

Ref

Bình luận

Bài viết tương tự

- vừa được xem lúc

async await javascript without try catch

javascript async await. Phần này chúng ta không nói nhiều về khái niệm async await là gì? Mà ở đây chúng ta đi sâu hơn về một vấn đề có nhiều trường hợp thực tế xảy ra khi triển khai với lập trình bất đồng bộ và async function javascript.

0 0 30

- vừa được xem lúc

Async/ await trong phiên bản Swift 5.5

WWDC21 đã kết thúc, nghĩa là phiên bản beta Swift 5.5 đầu tiên sắp ra mắt và nó đi kèm với một loạt các cải tiến - async / await, actors, throwing properties và nhiều cải tiến khác.

0 0 55

- vừa được xem lúc

Tìm hiểu về Synchronous và Asynchronous trong Javascript

1. Giới Thiệu.

0 0 88

- vừa được xem lúc

Flutter - Lập trình bất đồng bộ với Future, async, await.

Vào thẳng chủ đề nhé các bạn, hôm nay mình đọc được một bài viết về lập trình bất đồng bộ trong Dart. Ôi cái chủ đề này nghe quen quen nhợ, .

0 0 44

- vừa được xem lúc

[JS] Promise và async/await: Cuộc chiến không hồi kết hay là sự đồng hành đáng ghi nhận

Xin chào các bạn. Ngày trước khi xử lý bất đồng bộ, cách cơ bản và được hầu hết mọi developer sử dụng đó chính là callback function.

0 0 89