📜 ⬆️ ⬇️

Generators in ES6 and asynchronous code in a new way

When we start to deal with ES6, the generators are almost at the end of the list of innovations that we pay attention to. Often, we simply perceive generators as an easy way to create custom iterators, but in fact they can provide us with much more interesting features and are most likely one of the most interesting innovations in ES6.

What are generators and how do they work?


Generators are functions that can be started, paused, and resumed at various stages of execution. Essentially, this special function returns an iterator. Functions generators are indicated by an asterisk after the keyword function , and all magic is hidden in the use of the special keyword yield . It determines that the iterator should return after the next call to the next () method.

And immediately an example of a simple generator in ES6:

function* myGenerator() { yield 'first'; let input = yield 'second'; yield input; } //    let iterator = myGenerator(); //  ,    yield console.log(iterator.next()): // { value: 'first', done: false } // (   ),    yield console.log(iterator.next()); // { value: 'second', done: false } //  ( )    yield console.log(iterator.next('third')); // { value: 'third', done: false } //   (yield  ) console.log(iterator.next()); // { value: undefined, done: true } 

So what happens here?
')

Generators as iterators


At the beginning of the article it was mentioned that often generators are used as a simple mechanism for creating iterators. All this can be done using for for syntax:

For example:

 function* myGenerator() { yield 'first'; yield 'second'; yield 'third'; } for (var v of myGenerator()) { console.log(v); } 

Or for example:

 function* myGenerator(start, stop) { for (var i = start; i < stop; i++) yield i; } for (var v of myGenerator()) { console.log(v); } 

Ok, iterators are great, but let's see what things can be done more abruptly using generators.

So, we need to log in to some kind of backend, then use an authentication token to retrieve data from the API. If we apply the generator mechanism, it will look like this:

 co(function* () { var result = yield login(username, password); var posts = yield getPosts(result.token); return posts; }).then(value => { console.log(value); }, err => { console.error(err); }); function login(username, password) { return fetch('/login', { method: 'post', body: JSON.stringify({ username: username password: password }) }).then(response => response.json()); } function getPosts(token) { return fetch('/posts', { headers: new Headers({ 'X-Security-Token': token }) }).then(response => response.json()); } 

This example uses the co library, which makes your asynchronous code look like synchronous using the promise mechanism. Here for each yield, promise returns the result to the generator function and saves it to a variable.

Express vs Koa


Eeee, Koa? What is it all about?

Koa is a new generation framework (from the subtitle of the official website), which improves the middleware writing mechanism, using the same generators and the co library, and thus saving your applications from the hell of callbacks .
From a philosophical point of view, Koa seeks to “fix and replace the node,” and Express, on the contrary, “expand the node” (from the Koa documentation).


Middleware in Koa is built on the mechanism of generators and is a “cascade”. Simply put, the mechanism “downstream” ( downstream ) through all middleware is first launched, and then “upstream” through all middleware ( upstream ).

So, an example:

 var koa = require('koa'); var app = koa(); // x-response-time app.use(function *(next){ var start = new Date; yield next; var ms = new Date - start; this.set('X-Response-Time', ms + 'ms'); }); // logger app.use(function *(next){ var start = new Date; yield next; var ms = new Date - start; console.log('%s %s - %s', this.method, this.url, ms); }); // response app.use(function *(){ this.body = 'Hello World'; }); app.listen(3000); 

This example will return to us 'Hello World', but first:

  1. The request will go through the middleware x-response-time to do everything that goes to the yield next line.
  2. Then, next yield will stop the current function and transfer control to another middleware logger , it will also do everything to its next yield.
  3. In the logger, yield next will transfer control to the latest middleware.
  4. He in turn will form the answer this.body = 'Hello World' .
  5. We don’t have more middleware to transfer downstream , so the upstream process will start. Those. each middleware resumes its work after yield next , thus implementing an “upstream” mechanism.

Cool in general, what else to say. More about Koa can be viewed on the official website and on github .

Conclusion


ES6 generators are a powerful mechanism that allows you to write cleaner and more understandable asynchronous code. Instead of using a bunch of callbacks throughout the code, we can now write asynchronous code that looks similar to synchronous, but in fact with the help of generators and the yield keyword, it “waits” for the asynchronous operation to complete.

Source: https://habr.com/ru/post/277033/


All Articles