Basic knowledge of Generator _ in JavaScriptES6

Source: Internet
Author: User
Tags iterable es6 class
This article mainly introduces the Generator in JavaScriptES6. JS of ES6 brings many simplified improvements. For more information, see the new features discussed today, this feature is the most amazing feature in es6.

What does "magic" mean here? For beginners, this feature is completely different from the previous JavaScript code, and is even obscure. In a sense, it completely changes the common behavior of the language. This is not a "magic.

This feature also simplifies the program code and changes the complex "Callback stack" into a straight line execution form.

Do I have too many preparations? Next, let's take a look at it.
Introduction

What is Generator?

See the following code:

function* quips(name) { yield "hello " + name + "!"; yield "i hope you are enjoying the blog posts"; if (name.startsWith("X")) {  yield "it's cool how your name starts with X, " + name; } yield "see you later!";} function* quips(name) { yield "hello " + name + "!"; yield "i hope you are enjoying the blog posts"; if (name.startsWith("X")) {  yield "it's cool how your name starts with X, " + name; } yield "see you later!";}

The above code is part of the imitation of Talking cat (when the next very popular application). Click here to try it out. If you are confused about the code, let's go back to the following explanation.

This looks like a function, which is called the Generator function. It has a lot in common with our common functions, but we can see the following two differences:

A common function starts with a function, but a Generator function starts with a function.
In the Generator function, yield is a keyword, which is a bit like return. The difference is that all functions (including the Generator function) can be returned only once, while the Generator function can be yield any time. The yield expression suspends the execution of the Generator function and resumes execution from where it is paused.

Common functions cannot be paused, while Generator functions can. This is the biggest difference between them.
Principle

What happens when quips () is called?

> var iter = quips("jorendorff"); [object Generator]> iter.next() { value: "hello jorendorff!", done: false }> iter.next() { value: "i hope you are enjoying the blog posts", done: false }> iter.next() { value: "see you later!", done: false }> iter.next() { value: undefined, done: true } > var iter = quips("jorendorff"); [object Generator]> iter.next() { value: "hello jorendorff!", done: false }> iter.next() { value: "i hope you are enjoying the blog posts", done: false }> iter.next() { value: "see you later!", done: false }> iter.next() { value: undefined, done: true }

We are very familiar with the behavior of common functions. When a function is called, it is executed immediately until the function returns or throws an exception. This is the second nature of all JS programmers.

The call method of the Generator function is the same as that of a common function: quips ("jorendorff"), but it is not executed immediately when a Generator function is called, instead, a Generator object (iter in the code above) is returned, and the function is immediately suspended in the first line of the function code.

Every time you call the. next () method of the Generator object, the function starts to execute until the next yield expression is encountered.

This is why each time we call iter. next (), we get a different string, which is the value generated by the yield Expression in the function.

When the last iter. next () is executed, it reaches the end of the Generator function. Therefore, the. done attribute value of the returned result is true and the. value Attribute value is undefined.

Now, go back to the DEMO of Talking cat and try to add some yield expressions in the code to see what will happen.

Technically speaking, whenever the Generator function execution encounters a yield expression, the stack frame of the function-local variables, function parameters, temporary values and the current execution location are removed from the stack, however, the Generator object retains the reference to the stack frame, so it will be called next time. you can resume the next () method and continue the execution.

It is worth noting that Generator is not multithreading. In a multi-threaded language, multi-segment code can be executed at the same time, and the execution results are uncertain and have better performance. This is not the case with the Generator function. When a Generator function is executed, it is executed in the same thread as its caller. The execution sequence is determined and ordered each time, the execution sequence does not change. Unlike threads, the Generator function can be paused at the internal yield flag.

By introducing the pause, execution, and restoration of the Generator function, we know what the Generator function is. Now, a question is thrown: What is the purpose of the Generator function?
Iterator

Through the previous article, we know that the iterator is not a built-in class of ES6, but just an extension of the language. You can implement [Symbol. iterator] () and. the next () method to define an iterator.

However, to implement an interface, you still need to write some code. Let's take a look at how to implement an iterator in practice and take implementing a range iterator as an example, this iterator simply accumulates from one number to another, a bit like a for (;) loop in C language.

// This should "ding" three timesfor (var value of range(0, 3)) { alert("Ding! at floor #" + value);} // This should "ding" three timesfor (var value of range(0, 3)) { alert("Ding! at floor #" + value);}

Now there is a solution that uses the ES6 class. (If you are not familiar with the class syntax, I will introduce it in future articles .)

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};  } }}// Return a new iterator that counts up from 'start' to 'stop'.function range(start, stop) { return new RangeIterator(start, stop);} 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};  } }} // Return a new iterator that counts up from 'start' to 'stop'.function range(start, stop) { return new RangeIterator(start, stop);}

View the DEMO.

This implementation method is similar to that of Java and Swift. It looks good, but it cannot be said that the above Code is completely correct, and the Code does not have any bugs? This is hard to say. We don't see any traditional for (;) loop Code: The iterator protocol forces us to break up the loop.

At this point, you may not be so keen on the iterators. They are easy to use, but it seems difficult to implement them.

We can introduce a new implementation method to make it easier to implement the iterator. Can Generator described above be used here? Let's try:

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

View the DEMO.

The above four lines of code can completely replace the previous 23-line implementation and replace the entire RangeIterator class. This is because Generator is inherently an iterator, and all generators are native. next () and [Symbol. iterator] () method. You only need to implement the loop logic.

Without using Generator to implement an iterator, just like being forced to write a long and long email, you can simply express what you mean, the implementation of RangeIterator is lengthy and confusing because it does not use the loop syntax to implement a loop function. Using Generator is the implementation method we need to master.

What functions can we use as the Generator of the iterator?

So that any object can be traversed-write a Genetator function to traverse this. Every time a value is traversed, yield will be taken. Then, the Generator function will be used as the [Symbol. iterator] method implementation.
Simplified functions for returning Arrays-assume that an array function is returned every time a call is made, for example:

// Divide the one-dimensional array 'icons'// into arrays of length 'rowLength'.function splitIntoRows(icons, rowLength) { var rows = []; for (var i = 0; i < icons.length; i += rowLength) {  rows.push(icons.slice(i, i + rowLength)); } return rows;} // Divide the one-dimensional array 'icons'// into arrays of length 'rowLength'.function splitIntoRows(icons, rowLength) { var rows = []; for (var i = 0; i < icons.length; i += rowLength) {  rows.push(icons.slice(i, i + rowLength)); } return rows;}

Using Generator can simplify such functions:

function* splitIntoRows(icons, rowLength) { for (var i = 0; i < icons.length; i += rowLength) {  yield icons.slice(i, i + rowLength); }} function* splitIntoRows(icons, rowLength) { for (var i = 0; i < icons.length; i += rowLength) {  yield icons.slice(i, i + rowLength); }}

The only difference between the two is that the former calculates all the results during the call and returns the results using an array. The latter returns an iterator and the results are calculated only when needed, then return one by one.

Infinite result set-we cannot construct an infinite array, but we can return a Generator that generates endless sequences, and each caller can obtain any number of required values.
Refactor a complex loop-Do You Want To refactor a complex and lengthy function into two simple functions? Generator is a new Swiss Army knife in your refactoring toolbox. For a complex loop, we can reconstruct the code of the generated dataset into a Generator function, and then use for-of traversal: for (var data of myNewGenerator (args )).
The tool for building the iterator-ES6 does not provide a scalable library to perform operations such as filter and map on the dataset, but Generator can implement this function with several lines of code.

For example, assume that you need to implement the same functions as Array. prototype. filter on Nodelist. A piece of cake:

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

So, is Generator very practical? Of course, this is the simplest and most straightforward way to implement a custom iterator. In ES6, The iterator is a new standard for data sets and loops.

However, this is not all the functions of Generator.
Asynchronous code

Asynchronous APIs usually require a callback function, which means that each time you write an anonymous function to process asynchronous results. If three asynchronous transactions are processed at the same time, we can see three indented layers of code, not just three lines of code.

See the following code:

}).on('close', function () { done(undefined, undefined);}).on('error', function (error) { done(error);}); }).on('close', function () { done(undefined, undefined);}).on('error', function (error) { done(error);});

Asynchronous APIs generally have error handling conventions. Different APIs have different conventions. In most cases, errors are discarded by default, and even some are discarded by default.

Until now, these problems are still the price we have to pay for processing asynchronous programming, and we have accepted asynchronous code, but it is not as simple and friendly as Synchronous Code.

Generator gives us hope that we can stop using the above method.

Q. async () is an experimental attempt to combine Generator and Promise to process asynchronous code, so that our asynchronous code is similar to the corresponding Synchronous Code.

For example:

// Synchronous code to make some noise.function makeNoise() { shake(); rattle(); roll();}// Asynchronous code to make some noise.// Returns a Promise object that becomes resolved// when we're done making noise.function makeNoise_async() { return Q.async(function* () {  yield shake_async();  yield rattle_async();  yield roll_async(); });} // Synchronous code to make some noise.function makeNoise() { shake(); rattle(); roll();} // Asynchronous code to make some noise.// Returns a Promise object that becomes resolved// when we're done making noise.function makeNoise_async() { return Q.async(function* () {  yield shake_async();  yield rattle_async();  yield roll_async(); });}

The biggest difference is that you need to add the yield keyword before each Asynchronous Method call.

In Q. in async, adding an if statement or try-catch exception handling is the same as in Synchronous Code. Compared with other asynchronous code writing methods, this reduces the learning cost.

Generator provides us with an asynchronous programming model that is more suitable for human brain thinking. But better syntax may be more helpful. In ES7, an asynchronous processing function based on Promise and Generator is being planned and inspired by similar features in C.
Compatibility

On the server side, you can use Generator directly in io. js (or use the -- harmony startup parameter in NodeJs To Start Node ).

On the browser side, only Firefox 27 and Chrome 39 or later versions support Generator. If you want to use it directly on the Web, you can use Babel or Google's Traceur to convert ES6 code to Web-friendly ES5 code.

ASIDE: The JS version of Generator was first implemented by Brendan Eich. He borrowed the implementation of Python Generator, which was inspired by the Icon. As early as 2006, Firefox 2.0 absorbed the Generator. However, the road to standardization is bumpy. Along the way, its syntax and behavior have changed a lot. ES6 Generator in Firefox and Chrome is implemented by Andy Wingo, this work was sponsored by Bloomberg.
Yield;

There are still some unmentioned parts about Generator. We have not involved the use of the. throw () and. return () methods, the optional parameters of the. next () method, and the yield * syntax. However, I think this article is long enough. Just like Generator, we will suspend it and find the remaining time.

We have already introduced two very important features in ES6, so now we can say that ES6 will change our lives. seemingly simple features are of great use.

Related Article

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: info-contact@alibabacloud.com 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.