Unlimited WordPress themes, graphics, videos & courses! Unlimited asset downloads! From $16.50/m
Advertisement
  1. Code
  2. JavaScript
Code

Tối ưu code trong JavaScript với Iterators và Generators

by
Difficulty:AdvancedLength:MediumLanguages:

Vietnamese (Tiếng Việt) translation by Vy Cam Nguyen (you can also view the original English article)

Giới thiệu

Bạn đã từng cần chạy vòng lặp một danh sách, nhưng hoạt động này tốn một khoảng thời gian đáng kể để hoàn thành? Bạn đã bao giờ gặp phải vấn đề trong chương trình vì một hoạt động hao tốn quá nhiều bộ nhớ? Điều này đã xảy ra khi tôi cố gắng triển khai một hàm tạo ra các số nguyên tố.

Tạo ra số 1 triệu nguyên tố mất nhiều thời gian hơn tôi muốn. Nhưng tạo ra 10 triệu con số là bất khả thi. Chương trình của tôi sẽ bị hỏng hoặc bị treo. Tôi đã sử dụng Sàng nguyên tố Eratosthenes, là một thuật toán được cho là hiệu quả hơn trong việc tạo ra số nguyên tố hơn so với phương pháp brute force.

Nếu bạn ở trong tình huống tương tự, thì bạn có thể thử dùng một thuật toán khác. Có những thuật toán tìm kiếm (searching) và sắp xếp (sorting) hiệu quả hơn cho lượng dữ liệu lớn. Nhược điểm là những thuật toán này sẽ khó nắm bắt hơn. Một lựa chọn khác là sử dụng một ngôn ngữ lập trình khác.

Một ngôn ngữ biên dịch (compiled) có thể xử lý code nhanh hơn rất nhiều. Nhưng việc này có lẽ không tiện lợi. Có lẽ bạn cũng thử dùng multiple thread (đa luồng). Lần nữa, việc này cũng không thực tế vì ngôn ngữ lập trình của bạn phải hỗ trợ tính năng này.

Thật may mắn, JavaScript là một chọn lựa khác. Nếu bạn có nhiệm vụ đòi hỏi nhiều tính toán, thì bạn có thể dùng iterator và generator để tăng hiệu quả. Iterator là một thuộc tính của JavaScript collection.

Iterator cải thiện hiệu quả qua việc cho phép bạn sử dụng các item trong một list một lúc như thể chúng là một stream. Generator là một loại hàm đặc biệt có thể tạm dừng việc xử lý. Kích hoạt một generator cho phép bạn tạo ra một đoạn dữ liệu tại một thời điểm mà không cần lưu nó vào list trước.

Iterators

Đầu tiên, hãy xem lại những phương pháp khác nhau bạn có thể chạy vòng lặp qua những collection trong JavaScript. Một vòng lặp theo kiểu for(initial; conditionl; step){...} sẽ xử lý các lệnh bên trong nó qua một số lần cụ thể. Tương tự, vòng lặp while sẽ xử lý các lệnh của nó miễn là điều kiện của nó đúng.

Bạn có thể sử dụng vòng lặp để di chuyển trong list bằng cách tăng dần biến index tại mỗi lần lặp. Iteration là phần xử lý của một vòng lặp. Những vòng lặp này không biết cấu trúc của list. Chúng hoạt động như những bộ đếm số.

Vòng lặp for/infor/of được thiết kế để xử lý những cấu trúc dữ liệu xác định. Việc chạy vòng lặp trong cấu trúc dữ liệu có nghĩa là bạn đang từng bước tiếp cận những thành phần của vòng lặp đó. Vòng lặp for/in lặp đi lặp lại qua các khoá trong đối tượng JavaScript thuần, vòng lặp for/of lặp qua các giá trị của một iterable. Iterable là gì? Đơn giản mà nói, iterable là một đối tượng có một iterator. Ví dụ của iterable là array và set. Iterator là một thuộc tính của đối tượng cung cấp một cơ chế cho việc di chuyển đối tượng.

Điều khiến Iterator đặc biệt là cách nó di chuyển trong một collection. Những vòng lặp khác cần load toàn bộ collection trước để chạy vòng lặp, trong khi một iterator chỉ cần biết vị trí hiện tại của collection.

Bạn truy xuất item hiện thời bằng cách gọi phương thức next của iterator. Phương thức next sẽ trả về giá trị của item hiện thời và giá trị boolean để báo hiệu bạn đã tiến đến phần cuối cùng của collection. Sau đây là một ví dụ để tạo ra một iterator từ một array.

Bạn cũng có thể chạy vòng lặp qua các giá trị của iterator sử dụng vòng lặp for/of. Sử dụng phương thức này khi bạn biết bạn muốn truy xuất tất cả item của đối tượng. Đây là cách bạn sử dụng vòng lặp cho danh sách trước đó:

Sao bạn cần dùng iterator? Việc dùng iterator rất tiện lợi khi cần nhiều nỗ lực tính toán cho việc xử lý một danh sách. Nếu bạn có nguồn data rất lớn, thì có thể phát sinh vấn đề trong chương trình của bạn nếu bạn cố gắng chạy vòng lặp bởi vì toàn bộ collection cần được load.

Với iterator, bạn có thể load nhiều đoạn data nhỏ. Việc này hiệu quả hơn bởi vì bạn chỉ cần xử lý từng phần của danh sách mà không cần tạo thêm hao tổn cho việc xử lý toàn bộ danh sách.

Một ví dụ là bạn đã load dữ liệu từ một file hoặc database, và bạn muốn dần hiển thị thông tin trên màn hình. Bạn có thể tạo ra một iterator từ data và thiết lập một event handler để lấy một số item mỗi lần sự kiện xảy ra. Đây là ví dụ cho triển khai kiểu này:

Generators

Nếu bạn muốn xây dựng một collection, bạn có thể làm thế với một generator. Hàm generator có thể trả về một giá trị mỗi lần bằng cách dừng việc xử lý tại mỗi iteration. Khi bạn tạo một đối tượng generator, những item này có thể được truy xuất qua một iterator. Đây là cú pháp chung khi tạo một hàm generator:

Dấu * cho biết đây là một hàm generator. Từ khoá yield dừng hàm của chúng ta và cho biết trạng thái (state) của một generator tại một thời điểm xác định. Sao bạn là muốn dùng generator? Bạn sẽ dùng generator khi bạn muốn dùng thuật toán để tạo một giá trị trong collection. Nó đặc biệt hữu dụng nếu bạn có một collection rất lớn (hoặc vô cực). Hãy xem một ví dụ để hiểu rõ điều này giúp ích thế nào.

Giả thiết bạn có một trò chơi pool trực tuyến, và bạn muốn kết hợp người chơi vào các phòng game. Mục tiêu là tạo ra tất cả cách cho bạn chọn 2 người chơi từ danh sách 2000 game thủ. Các tổ hợp 2 người chơi được tạo ra từ danh sách ['a', 'b', 'c', 'd'] sẽ là ab, ac, ad, bc, bd, cd. Đày là giải pháp khi dùng vòng lặp lồng nhau:

Giờ hãy thử xử lý hàm với danh sách 2000 phần tử. (bạn có thể khởi tạo danh sách bằng vòng lặp for để thêm từ 1 đền 2000 vào một array). Khi bạn chạy code này thì điều gì xảy ra?

Khi tôi chạy code trên một editor trực tuyến, trang web bị lỗi. Khi tôi cố gắng thử trên console của Chrome, tôi có thể thấy kết quả hiển thị rất chậm. Tuy nhiên, CPU máy tính của tôi bắt đầu tăng tốc, và tôi buộc phải thoát Chrome. Đây là code đã tinh chỉnh bằng hàm generator:

Một ví dụ khác là nếu chúng tôi muốn phát sinh con số trong dãy Fibonacci lên đến vô cực. Đây là một triển khai:

Thường thì một vòng lặp vô hạn sẽ gây lỗi. Hàm fibGen có thể chạy mãi bởi vì không có điều kiện dừng xử lý. Nhưng khi nó là generator, bạn kiểm soát được khi nào mỗi bước được xử lý.

Tóm tắt

Iterator và generator hữu ích khi bạn muốn xử lý một collection theo từng bước. Bạn gia tăng hiệu quả bằng cách theo dõi trạng thái của collection, thay vì tất cả item trong collection. Các item trong collection được đánh giá từng cái tại một thời điểm, và phần đánh giá các item còn lại được hoãn lại sau đó.

Iterator mang đến một cách hiệu quả để di chuyển và xử lý những list lớn. Generator giúp cho việc tạo list hiệu quả. Bạn nên thử qua những kỹ thuật này khi bạn sử dụng một thuật toán phức tạp hoặc triển khai lập trình song song để tối ưu code của bạn.

Nếu bạn đang tìm kiếm những tài nguyên bổ sung để học thêm hoặc sử dụng trong công việc, hãy xem qua chúng tôi sẵn có gì trên Envato Market.

Các tài nguyên

Advertisement
Advertisement
Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.