Cùng khám phá sức mạnh của ES6 Generators

1628

Generators có thể xem như làcách áp dụng của iterables

Điều khiến generators trở nên đặc biệt bởi vì chúng là nhữngfunctions có khả năng hoãn lại quá trình execution mà vẫn giữ nguyên được context.

Đây là một tính năng rất quan trọng khi ta phải dùng tới những executions đòi hỏi phải có quãng pause nhưng context phải được để nguyên nhằm để recover lại trong tương lai khi cần đến. 

Bạn có từng nghe qua quá trình phát triển async chưa?

Syntax – Cú pháp

Syntax (Cú pháp)cho generators bắt đầu với function*  declaration của chính nó (nhớ lưu ý cáiasterisk) và  yield  dành cho khi generator muốn dừng (pause) execution.

Với  next function, chúng ta có thể kiểm soát được quá trình tạo ra một generator từ   generator  sẵn có.

Khi chạy  next function, thì code của   generator sẽ được thực hiện (execute) và cho đến khi gặp  yield  thì sẽ ngừng lại.

Lúc đó,  yield sẽ xen vào và khiến cho  generator execution bị đình chỉ (pause).

yield  được sinh ra cùng lúc với generator và cho phép chúng ta đưa ra các giá trị mà mình muốn. Tuy nhiên, nó chỉ thực hiện được khi ở trong phạm vi của generator.

Nếu thử dùng yield  với một giá trị trong callback thì cho dù đã declared trong generator thì nó vẫn sẽ bị lỗi.

 

yield*

yield* được tạo ra nhằm có khả năng gọi một generator nằm trong một generator khác.

 

Bạn có thể thấy b iterator, thuộc  bar   generator, không hề chạy như đúng ý ta khi call foo .

Đó là mặc dù foo   execution cho ra một iterator, nhưng ta sẽ không có lặp lại (iterate) nó được.

Vì thế mà ES6 cần có operator  yield*

Đồng thời nó cũng hoàn toàn có thể áp dụng với data consumers

 

 yield*  có khả năng kiểm tra và chạy qua hết tất cả ngõ ngách trong generator để yield ra phần nó cần.

 

Generators cũng chính là Iterators

Generators thực chất là những iterables đơn giản.Nói cách khác chúng cũng sẽ theo luật của iterable và  iterator .

Luật của iterable cho ta biết một object sẽ nên return một function itera với key là  Symbol.iterator.

 

Còn luật của iterator cho ta biết iterator nên là một object chỉ tới yếu tố tiếp theo của iteration. Object này phải chứa một function gọi là next

 

Bởi vì generators là iterables nên chúng ta có thể dùng data consumer  for-of, để iterate (lặp lại) giá trị của generators (values).

 

Return

Chúng ta còn có thể add vào return  cho generator, thế nhưng return  sẽ hoạt động hơi khác đi tùy thuộc vào cách generators’ data được iterated.

 

Khi ta thực hiện iteration bằng tay, sử dụng  next , sẽ nhận được returned value (i.e. done  ) cũng chính là value  cuối của iterator object và khi done   đưa ra kết quả true.

Mặt khác, khi sử dụng defined data consume như for-of  hoặc destructuring  thì returned value sẽ bị bỏ qua.

 

yield*

Như bạn đã biết  yield* được tạo ra nhằm có khả năng gọi một generator nằm trong một generator khác.

Ngoài ra, nó còn cho phép chúng ta lưu trữ value returned bằng executed generator.

 

Throw

Chúng ta có thể dùng throw trong một generator và next   sẽ truyền exception ra.

Và khi một exception bị đẩy ra, iterator (lặp) sẽ bị phá và state của nó sẽ được set thành  done: true

Generators cũng chính là Data Consumers

Generators ngoài khả năng như một data producers, với yield, nó cũng có thể consume data khi dùng next.

 

Có một vài điểm khá thú vị sau đây

Generator Creation (1)

Ở stage này, chúng ta đang tạo ra generator g.

Và execution sẽ dừng lại tại điểm A.

Next đầu tiên (2)

Execution đầu tiên của next  giúp cho generator được executed cho tới khi gặp phải yield .

Tất cả các giá trị (value) trong stage này khi đi qua  next sẽ bị lơ đi. Nguyên nhân là vì vẫn chưa có gặp một   yield nào cả.

Và execution của chúng ta chỉ dừng lại tại điểm B  khi một  value nào đó được đưa ra bởi  yield .

Next tiếp theo (3)

Lúc này thì value đã đi qua yieldvà như vậy execution sẽ bị ngừng lại.

Hãy dùng Cases

Implement Iterables

Bởigenerators là một iterable implementation,khi chúng được tạo ra thì chúng ta cũng sẽ có một iterable object với từng yield đại diện cho một giá trị sẽ được đưa ra trên từng iteration. Nói cách khác chúng ta có thể dùng generators để tạo ra iterables.

Ví dụ sau đây sẽ thể hiện generator như là iterable  với khả năng lập một dãi các số nguyên cho tới khi nó đạt  max . Và ta cũng dùng for-of   để lập những giá trị trên.

Các bạn cũng cần lưu ý rằng yield sẽ khiến các execution bị dừng lại tại một điểm và các iteration sẽ khiến cho execution chạy tiếp tại các điểm đó.

 

Asynchronous Code

Ta còn có thể dùng generators  với những async code như  promises .

Tiện thể thì cũng coi như là để giới thiệu về async/await trên ES8.

Ví dụ dưới đây cũng sẽ cho ta thấy cách tìm kiếm một JSON file nhờ vào  promises . Đây là ví dụ củaJake Archibaldthuộcdevelopers.google.com.

Nhờ vàoco libraryvà một generator, code của chúng ta sẽ nhìn giống như synchronous code.

 

Với async/await thì nó vẫn sẽ khá giống so với phiên bản trên

 

Lời Kết

Dưới đây là một map thể hiện mối quan hệ giữa generators và iterators, bởiAxel RauschmayertrênExploring ES6.

Generators chính là một cách thực hiện của iterable và nó dựa theo luật của  iterable của iterator  . Vì thế mà chúng có thể dùng để tạo ra iterables.

Tính năng tuyệt vời nhất của Generator là khiến execution bị hoãn lại. Với  yield nếu xài ES6.

Không những thế với  yield*, ta còn có thể gọi một generator nằm trong một generator khác.

Generators chính là cách thức để giúp việc phát triễn không đồng bộ trở thành đồng bộ.

Nguồn: Topdev via Medium