Learn ES6 generator (Generator)

Source: Internet
Author: User

Background

In JS usage scenario, the processing of asynchronous operation is an unavoidable problem, if do not do any abstraction, organization, just "follow the feeling", then face "in order to launch 3 Ajax request" Requirements, it is easy to write the following code (assuming that the introduction of jquery):

//1th Ajax request$.ajax ({URL:' http://echo.113.im ', datetype:' json ', type:' get ', data:{data:json.stringify ({Status:1,data:' Hello World '), type:' json ', timeout: +},Success:function (data) { if (data.status = = = 1) { //2nd Ajax Request$.ajax ({... omitted here500 WordsSuccessfunction (data) { if (data.status = = = 1) { //3rd Ajax request$.ajax ({... omitted here500 WordsSuccessfunction (data) { if (data.status = = = 1) { }}});}}});}}});

When there are more and more asynchronous operations executed sequentially, the more the callback level is, the legendary "Callback Demon Pyramid".

Generator's Lu Shan True Colors

The so-called "generator" is actually a function, but the behavior of this function is quite special:

    1. It does not directly execute the logic, but is used to generate another object (which is what the "generator" means)
    2. The function in the generated object can take the logic apart and execute it in a single piece, rather than as a normal function, it can only be done from beginning to end.

The syntax of the generator is similar to the normal function, which is special:

    1. A literal (function declaration/function expression) is preceded by one of the keywords function * , and this * allows a white-space character before and after this
    2. More operators in the function body yield

Give a chestnut:

function * GenA () {console.log (' from GenA, first. '); yield 1; Console.log (' from GenA, second. '); var value3 = yield 2; Console.log (' from GenA, third. ', value3);  return 3;} var a = GenA ();

Next, proceed in turn:

A.next (); //From GenA, first. //Object {value:1,done:false} a.next (); //From GenA, second. //Object {value:2,done:false} a.next (333); //From GenA, third. //333 //Object {value:3,done:true} a.next (); //Object {value:undefined,done:true}

This example reflects the basic usage of the generator and has the following points to note:

    1. When called GenA() , the logic in the body of the function does not execute (the console has no output) and is a.next() only executed when called directly
    2. ais an object that is called by the generator GenA() , noting that GenA() no object is returned, which is a much like the execution of a constructor, but does not allow addingnew
    3. a.next()when called, the logic in the body of the function begins to actually execute, each time it is called to the end of the yield statement, and the operand is yield returned as a result
    4. a.next()The returned result is an object, the yield number of operands is wrapped, and the attributes are taken done
    5. When done the property is a false , it means that the function logic is not finished and can be called a.next() to continue execution
    6. The result returned by the last time is the return result returned by the statement, and the done value is true . If you do not write return , the value isundefined
    7. value3 = yield 2This sentence means that the logic returns 2, and at the next call a.next() , assign the parameter to Value3. In other words, the sentence is paused only after the second half of the paragraph is executed, and the parameter is assigned to the VALUE3 when it is called again a.next() and proceeds to the following logic
    8. When the return value is in done true , the call can still continue, and the value returned isundefined
Use of generators in synchronous scenarios

To see how the generator is used in the synchronization scenario:

function * Square () {for(var i=1;; i++) {yield i*i;}} var square = square (); Square.next (); //1square.next (); //4square.next (); //9...

That's probably how it's used in a synchronized scenario, isn't it? I also feel that, in fact, and direct function calls are not very different. It is important to note, however, that we do not have a abort condition in the loop, because calling a method does not execute square.next() once, and does not do so without worrying about the problem of a dead loop.

The generator in the asynchronous scenario uses

How to use the generator to solve the "callback Demon Pyramid" in an asynchronous scenario? Look forward to it, I'm sorry, it can't be solved so easily ...

From the previous example, it can be realized that the usage of the generator does not include the processing of asynchronous, so there is no way to help us to close the asynchronous callback. So why do we see it as an artifact to fix the callback nesting? After flipping through a lot of information to find this article, the author initially thought that the generator does not solve the problem of callback nesting, but the following self-explanation, if the generator returned is a series of promise objects, the situation will be different, for a chestnut:

function Myajax () {return fetch (' http://echo.113.im?data=1 ');}

We use the window.fetch method to handle the AJAX request, and this method returns a Promise object. Then, we use a generator to wrap this operation:

function * mylogic () {var serverdata = yield Myajax (); console.log (' mylogic after Myajax '); console.log (' serverstatus:%s ', serverdata.status);}

This is used when using:

var mylogic = Mylogic (); var promise = Mylogic.next (). value; Promise.then (function (serverdata) {mylogic.next (serverdata);});

As you can see, we myAjax1() MyLogic() have completed the asynchronous operation here and in the function without using a callback.

Here are a few notable points to note:

    1. myAjax()The function returns a Promise object
    2. myLogicIn the first statement, returned to the outside world is the myAjax() return of the Promise object, and so on when the outside world calls the next() method when the data passed in, assigned toserverDate
    3. promiseThe state is handled by the third piece of code externally, and the method is called when it is finished myLogic.next() and will be serverData passed back to the MyLogic()

You will ask, this promise.done is not the callback operation? Bingo! This is where the essence lies! Let's take a look at what this piece of code does:

First, it myLogic.next() returns a Promise object ( promise ), and then, the promise.then callback function in the thing is to invoke the myLogic.next() method is OK, in addition to calling the next() method, nothing else. At this point, we will think of a programmer especially like the word, called "encapsulation"! Since this callback function is just a calling myLogic.next() method, why not wrap it up?

Asynchronous encapsulation

First, we maintain myAjax() and MyLogic define the invariant, and will myLogic.next() put in a function to call, this function is specifically responsible for the call myLogic.next() , get the returned Promise object, and then the promise is resolve when the call again myLogic.next() :

var mylogic = Mylogic (); function Genrunner () { //Call Next () get Promisevar yieldvalue = Mylogic.next (); var promise = Yieldvalue.value; if (promise) {Promise.then (data) {///promise is called again when resolve is Genrunner //To continue execution of the logical Genrunner (), later in the Mylogic ;});}}

In this way we encapsulate the process that is constantly being invoked myLogic.next() and kept promise.then() in place. Run a running run genRunner() :

Mylogic after Myajax1uncaught (in promise) Typeerror:cannot Read Property ' status ' of undefined (...)

The MyLogic yield Subsequent statement is actually executed, but serverData there is no value, because we myLogic.next() did not pass the value back at the time of the call. Modify the code slightly:

//Diff1:genrunner Accept parameter Val function Genrunner (val) { //diff2:. Next call to pass parameters, yield left can be assigned var Yieldvalue = Mylogic.next (val); var promise = Yieldvalue.value; if (promise) {Promise.then (data) {//DIFF3: Pass arguments when Genrunner is called Genrunner (data);});}}

This time it's all right:

Mylogic after myajax1serverstatus:200

At this point we have pulled out the core of the package, our business code MyLogic() is "asynchronous operation, synchronous writing", and we have witnessed how all this is done. So what's next? Why not encapsulate it more generally?

var Genrunner = function (genfunc) { return new Promise (function (Resolve, reject) { var gen = Genfunc ();var Innerrun = function (val) { var val = Gen.next (val);//If you have finished running, then resolveif (val.done) {Resolve (Val.value);return;}//If there is a return value, call '. Then '//Otherwise call directly next time Innerrun ()//For simplicity, assume that there is a value always promiseif (val.value) {Val.value.then (function (data) { Innerrun (data);});}else{Innerrun (Val.value);} }innerrun (); }); };

Here we've changed the package we just saw innerRun() and added the automatic call. The outer layer is then encapsulated genRunner() , returning a promise. genFuncafter the full call, Promise was resolve.

It used to be about this:

Genrunner (function* () { var serverdata = yield Myajax (); console.log (' mylogic after Myajax ');  console.log (' serverstatus:%s ', serverdata.status); }). Then (function (message) { console.log (message); });

Life is so beautiful!

Finally, use a KOA framework in someone else's article to end the code:

var koa = require ( app = Koa ();  app.use (  //this is the most important part of this example, we do a series of asynchronous operations , but there is no callback var city = yield geolocation.getcityasync ( this.req.ip); var forecast = yield weather.getforecastasync (city);  this.body = ' Today, ' + City + ' would be ' + Forecas T.temperature + ' degrees. ';  });  app.listen (8080);

Does it look familiar? Koa just like we just did, encapsulates the details of the processing and invocation methods of the generator return value next() (here, app.use() like the previous genRunner() function), making our logic code look so simple, which is the greatness of KOA, It is also the real reason why this feature of the ES6 generator can quickly cause so much sensation.

Learn ES6 generator (Generator)

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.