In layman's ES6 (iii): Generator generators

Source: Internet
Author: User
Tags iterable es6 class

Author Jason Orendorff GitHub home Page Https://

Introduction to the ES6 generator (generators)

What is a generator?

Let's start with an example:

function* quips (name) {  yield "Hello" + name + "!";  Yield "I hope you enjoy this introduction to ES6 's translation";  if (Name.startswith ("x")) {    yield "your name" + name + "  First letter is X, this is cool! ";  }  Yield "We'll see you next time! ";}

This is a talking cat, and this code is likely to represent the most important type of application on the Internet today. (Try clicking on this link and interacting with the cat, if you're feeling a little confused, go back and read on).

This code looks much like a function, which we call a generator function, which has a lot in common with normal functions, but the two have the following differences:

    • The normal function uses a function declaration, and the generator function uses the function* declaration.
    • Inside the generator function, there is a syntax similar to return: the keyword yield. The difference between the two is that the normal function can return only once, and the generator function can yield multiple times (or only one yield at a time, of course). During the execution of the generator, the yield expression is paused immediately, and subsequent recovery execution state is encountered.

This is the biggest difference between the normal function and the generator function, the normal function cannot be self-paused, and the generator function can.

What did the generator do?

What happens when you call the quips () generator function?

> var iter = quips ("Jorendorff");  [Object generator]> ()  {value: "Hello jorendorff!", Done:false}> ()  {value: "I hope you can enjoy this introduction E S6 ", Done:false}> ()  {value:" We'll see you next time! " ", Done:false}> ()  {value:undefined, done:true}

You've probably gotten used to the way ordinary functions are used, and when you call them, they start running immediately until they get a return or throw an exception, and as a JS programmer you must be familiar with this.

The generator call looks very similar: quips ("Jorendorff"). However, when you invoke a generator, it is not executed immediately, but instead returns a paused generator object (ITER in the instance code above). You can treat this generator object as a function call, but freeze it immediately, and it freezes just before the first line of code at the top of the generator function.

Whenever you call the. Next () method of a Builder object, the function call thaws itself and runs until the next yield expression, pausing again.

This is also the reason we call () each time in the code above, and we get the different string values generated by the yield expression in the quips () function body.

When the last () is called, we finally arrive at the end of the generator function, so the value of done in the returned result is true. The end of the arrival function means that there is no return value, so the value of value in the returned result is undefined.

Now go back to the Talking Cat's demo page and try to add a yield to the loop, what happens?

If described in professional terms, the generator's stack structure (local variables, parameters, temporary values, the current execution position inside the generator) is moved out of the stack whenever the generator executes the yields statement. However, the generator object retains a reference to the stack structure (backup), so it is called later. Next () to reactivate the stack structure and continue execution.

In particular, the generator is not a thread, and in a thread-supported language, multiple pieces of code can run concurrently, often resulting in race conditions and non-determinism, but also with good performance. The generator is completely different. When the generator runs, it is in the same thread as the caller, has a deterministic sequential execution sequence, and is never concurrent. Unlike a system thread, a generator is paused only if it has a point marked as yield in its function.

Now, we understand the generator's principles, and see the different states of the generator running and pausing the recovery. So what is the use of these strange features?

The generator is an iterator!

Last week, we learned about the ES6 iterator, which is an independent, built-in class in ES6 and an extension point for languages, and you can create a custom iterator by implementing the [Symbol.iterator] () and. Next () two methods.

Implementing an interface is not a trivial matter, we implement an iterator together. For example, we create a simple range iterator that simply adds up all the numbers between two numbers. The first is the for of the traditional C (;;) Cycle:

It should pop up three times "ding" for (var value of range (0, 3)) {  alert ("ding! At floor # "+ value);}

Use the ES6 class solution (if you don't know the syntax details, don't worry, we'll explain it in the next article):

Class Rangeiterator {  constructor (start, stop) {    this.value = start;    This.stop = stop;  }  [Symbol.iterator] () {return this;}  Next () {    var value = this.value;    if (value < this.stop) {      this.value++;      return {done:false, value:value};    } else {      return {done:true, value:undefined};}}  Returns a new iterator that can be counted from start to stop. function range (start, stop) {  return new Rangeiterator (start, stop);}

The implementation here is similar to an iterator in Java or swift, not very bad, but it's not entirely a problem. It's hard to tell if there's a bug in this code, which doesn't look like the traditional for (;;)) We're trying to imitate. Loop, the iterator protocol forces us to disassemble the loop part.

At this point you may not feel the iterators yet, they are cool to use, but they look a little hard to implement.

You probably wouldn't want to make iterators easier to build, suggesting we introduce a bizarre and brutal new control flow structure for the JS language, but since we have generators, can we apply them here? Try it together:

function* Range (start, stop) {for  (var i = start; i < stop; i++)    yield i;}

The 4-line Code implementation Builder can replace the implementation of the 23-line code that introduced the entire Rangeiterator class. The possible reason is that the generator is an iterator. All generators have an implementation of the built-in. Next () and [Symbol.iterator] () methods. You only need to write the behavior of the Loop section.

We all hate being forced to write a long message in the passive voice, and the process of implementing an iterator without a generator is similar and painful. When your language is no longer concise, the words will become difficult to understand. The implementation code for Rangeiterator is very long and very strange, because you need to add a description of the looping functionality without the help of the cyclic syntax. So the generator is the best solution!

How do we maximize the effectiveness of the generator as an iterator?

L enable arbitrary objects to iterate. Write a generator function to traverse this object, which yields each value at run time. The generator function is then used as the [Symbol.iterator] method for this object.

L simplify array building functions. Suppose you have a function that returns an array result each time it is called, like this:

Splits a one-dimensional array icons//by length rowlengthfunction splitintorows (icons, rowlength) {  var rows = [];  for (var i = 0; i < icons.length; i + = rowlength) {    Rows.push (icons.slice (i, i + rowlength));  }  return rows;}

The code created with the generator is relatively short:

function* splitintorows (icons, rowlength) {for  (var i = 0; i < icons.length; i + = rowlength) {    yield Icons.sli Ce (i, i + rowlength);  }}

The only difference in behavior is that the traditional notation evaluates all results immediately and returns the result of an array type, using the generator to return an iterator that evaluates the results one at a time, as needed.

    • Gets the result of the exception dimension. You can't build an infinite array, but you can return a generator that can generate a never-ending sequence, and each call can take any number of values from it.
    • Refactor complex loops. Have you ever written an ugly and big function? Would you like to split it into two simpler parts? Now, your refactoring toolbox has a new razor-generator. When you are faced with a complex loop, you can break out the code that generates the data, convert it to a standalone generator function, and then use the for (var data of mynewgenerator (args)) to traverse the data we need.
    • Build tools that are related to iterations. ES6 does not provide an extended library to filter, map, and perform special operations on any of the iterated datasets. With generators, we just have to write a few lines of code to implement similar tools.

For example, suppose you need a method that is equivalent to Array.prototype.filter and supports Dom nodelists, which can be written like this:

function* Filter (test, iterable) {for  (var item of iterable) {    if (test (item))      yield item;  }}

You see, the generator is magical! With their power it is very easy to implement a custom iterator, remembering that the iterator runs through ES6, which is the new standard for data and loops.

The above is just the tip of the generator, the most important features please continue to watch!

Generator and Async Code

This is some of the JS code I wrote some time ago

         };        })      });    });  });});

You may have seen similar fragments in your own code, and asynchronous APIs often require a callback function, which means you need to write extra asynchronous functions for each task execution. So if you have a piece of code that needs to do three tasks, you'll see a similar three-level indent code instead of a simple three-line code.

And then I wrote it like this:

}). On (' Close ', function () {Done  (undefined, undefined);}). On (' Error ', function (error) {done  (error);});

The Async API has error handling rules and exception handling is not supported. Different APIs have different rules, most error rules are default, and in some APIs even success hints are default.

These are the costs we have paid for asynchronous programming so far, and we are slowly beginning to accept the fact that asynchronous code is not as beautiful and concise as the equivalent synchronous code.

The generator provides you with a new idea to avoid the above problems.

Experimental Q.async () attempts to combine promises with the generator to produce an equivalent synchronous code for asynchronous code. As an example:

Make some noise in sync code. function Makenoise () {  shake ();  Rattle ();  Roll ();} Asynchronous code that makes some noise. Returns a Promise object//When we make noise, it becomes resolvedfunction Makenoise_async () {  return Q.async (function* () {    yield Shake_async ();    Yield Rattle_async ();    Yield Roll_async ();  });

The main difference is that the asynchronous version must add the yield keyword where the asynchronous function is called each time.

Adding a judgment or Try/catch block like an if statement in the Q.async version is as simple as adding a similar function to a synchronous version. This approach is more natural and less laborious than learning a new language compared to other asynchronous code writing methods.

If you've already seen this, you can try reading a more in-depth story about the generator from James Long.

The generator provides us with a new approach to the asynchronous programming model, which is better suited to the human brain. Relevant work is being carried out continuously. In addition, better syntax might help, ES7 has a proposal for asynchronous functions, built on promises and generators, and draws a lot of inspiration from the similar features in C #.

How do you apply these crazy new features?

On the server side, you can now use ES6 in io.js (you need to use the--harmony command-line option in node).

On the browser side, only Firefox 27+ and Chrome 39+ support the ES6 generator so far. If you want to use the generator on the web side, you need to use Babel or traceur to translate your ES6 code into a web-friendly ES5.

At first, the generator in JS was implemented by Brendan Eich, whose design referenced the Python generator, which was inspired by the icon. They migrated the code in Firefox 2.0 as early as 2006. However, the standardization of the road is bumpy, the relevant grammar and behavior are based on the original change. The ES6 generator in Firefox and Chrome is implemented by the compiler hacker Andy Wingo. The job was supported by Bloomberg (the famous Bloomberg, the one who heard it wrong). )。

In layman's ES6 (iii): Generator generators

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.