Introduction to the usage of Generator function in ES6

不言
Release: 2019-03-30 09:54:26
forward
2080 people have browsed it

This article brings you an introduction to the usage of the Generator function in ES6. It has certain reference value. Friends in need can refer to it. I hope it will be helpful to you.

1. What is the Generator function

The Generator function is an asynchronous programming solution proposed in the ES6 standard. The biggest difference between this kind of function and ordinary functions is that it can pause execution and resume execution from the paused position.

From a syntax perspective, the Generator function is a state machine that encapsulates many internal states.

In essence, the Generator function is a traverser object generator. (For the traverser object, you can refer to this article by teacher Ruan Yifeng) The Generator function returns a traverser object. By traversing this object, you can obtain each state inside the function in turn.

2. Basic syntax

1. Define Generator function

The difference between defining a Generator function and defining an ordinary function is:

# There is an * (asterisk) between the ##function keyword and the function name.

Yield is used inside the function to define the internal state of each function.
If there is a return statement inside the function, then it is the last state inside the function.

Let’s look at a simple example:


// 定义 function* sayHello() { yield 'hello'; yield 'world'; return 'ending'; } // 调用 // 注意,hw获取到的值是一个遍历器对象 let g = sayHello();
Copy after login
The above example defines a Generator function named

sayHello, which has two ## inside. A #yieldexpression and areturnexpression. Therefore, there are three states inside the function:hello,worldandreturnstatement (end execution). Finally, call this function to get a traverser object and assign it to the variableg.The calling method of the Generator function is exactly the same as the ordinary function,

Function name ()

. The difference is: After the

function is called, the internal code (starting from the first line) will not be executed immediately.
  • There will be a return value after the function is called. This value is a pointer object pointing to the internal state. It is essentially a traverser object containing the internal state of the function.
  • The Generator function will not be executed immediately after it is called. So, how do we let it start executing the internal code? How to get every state inside it? At this point, we must call the .next() method of the returned generator object to start the execution of the code and move the pointer to the next state.

Take the above example as an example:

g.next(); // { value: 'hello', done: false } g.next(); // { value: 'world', done: false } g.next(); // { value: 'ending', done: true } g.next(); // { value: undefined, done: true }
Copy after login

In the above code, a total of four times

g

.next()## of this traverser object is called #method. When called for the first time, thesayHelloGenerator function starts executing and execution will be suspended until it encounters the firstyieldexpression. The.next()method will return an object whosevalueattribute is the value of the currentyieldexpressionhello,The value of the doneattribute isfalse, indicating that the traversal has not ended.The second time you call.next()

, the execution will reach the second

yieldexpression, execution will be suspended, and the corresponding object will be returned.The third call to.next()

, the function is executed to the last

returnstatement, which marks the end of the traversal of the traverser objectg, so thevalueattribute value in the returned object is the valueendingfollowed byreturn, and thedoneattribute value istrue, indicating that the traversal has ended.The fourth and subsequent calls to the .next() method will return{value: undefined, done: true }

.

2. Yield expression

The traverser object returned by the Generator function will traverse to the next internal state only by calling the

.next()

method, so this In fact, it provides a function that can pause execution. The

yieldexpression is the pause flag.The running logic of the.next()

method of the traverser object is as follows.

When encountering the

yield
    expression, it suspends the execution of subsequent operations and returns the value of the expression immediately following
  1. yieldThevalueattribute value of the object.The next time the.next()
  2. method is called, execution will continue until the next
  3. yieldexpression is encountered.If no newyield
  4. expression is encountered, it will run until the end of the function until the
  5. returnstatement, andreturnThe value of the expression following the statement is used as thevalueattribute value of the returned object.If the function does not have areturn
  6. statement, the
  7. valueattribute value of the returned object isundefined.
  8. It is worth noting:

  1. yield关键字只能出现在Generator函数中,出现在别的函数中会报错。
// 出现在普通函数中,报错 (function () { yield 'hello'; })() // forEach不是Generator函数,报错 [1, 2, 3, 4, 5].forEach(val => { yield val });
Copy after login
  1. yield关键字后面跟的表达式,是惰性求值的。只有当调用.next()方法、内部状态暂停到当前yield时,才会计算其后面跟的表达式的值。这等于为JavaScript提供了手动的“惰性求值”的语法功能。
function* step() { yield 'step1'; // 下面的yield后面的表达式不会立即求值, // 只有暂停到这一行时,才会计算表达式的值。 yield 'step' + 2; yield 'setp3'; return 'end'; }
Copy after login
  1. yield表达式本身是没有返回值的,或者说它的返回值为undefined。使用.next()传参可以为其设置返回值。(后面会讲到)
function* gen() { for (let i = 0; i < 5; i++) { let res = yield; // yield表达式本身没有返回值 console.log(res); // undefined } } let g = gen(); g.next(); // {value: 0, done: false} g.next(); // {value: 1, done: false} g.next(); // {value: 2, done: false}
Copy after login

yield与return的异同:

相同点:

  • 两者都能返回跟在其后面的表达式的值。

不同点:

  • yield表达式只是暂停函数向后执行,return是直接结束函数执行。
  • yield表达式可以出现多次,后面还可以有代码。return只能出现一次,后面的代码不会执行,在一些情况下还会报错。
  • 正常函数只能返回一个值,因为只能执行一次return。Generator函数可以返回一系列的值,因为可以有任意多个yield。

3、.next()方法传参

前面我们说到过,yield表达式自身没有返回值,或者说返回值永远是undefined。但是,我们可以通过给.next()方法传入一个参数,来设置上一个(是上一个)yield表达式返回值。

来看一个例子:

function* conoleNum() { console.log('Started'); console.log(`data: ${yield}`); console.log(`data: ${yield}`); return 'Ending'; } let g = conoleNum(); g.next(); // 控制台输出:'Started' g.next('a'); // 控制台输出:'data: a' // 不传入参数'a',就会输出'data: undefined' g.next('b'); // 控制台输出:'data: b' // 不传入参数'a',就会输出'data: undefined'
Copy after login

上面的例子,需要强调一个不易理解的地方。

第一次调用.next(),此时函数暂停在代码第三行的yield表达式处。记得吗?yield会暂停函数执行,此时打印它的console.log(),也就是代码第三行的console,由于暂停并没有被执行,所以不会打印出结果,只输出了代码第二行的'Started'。

当第二次调用.next()方法时,传入参数'a',函数暂停在代码第四行的yield语句处。此时参数'a'会被当做上一个yield表达式的返回值,也就是代码第三行的yiled表达式的返回值,所以此时控制台输出'data: a'。而代码第四行的console.log()由于暂停,没有被输出。

第三次调用,同理。所以输出'data: b'

4、Generator.prototype.throw()

Generator函数返回的遍历器对象,都有一个.throw()方法,可以在函数体外抛出错误,然后在Generator函数体内捕获

function* gen() { try { yield; } catch (e) { console.log('内部捕获', e); } }; var g = gen(); // 下面执行一次.next() // 是为了让gen函数体执行进入try语句中的yield处 // 这样抛出错误,gen函数内部的catch语句才能捕获错误 g.next(); try { g.throw('a'); g.throw('b'); } catch (e) { console.log('外部捕获', e); }
Copy after login

上面例子中,遍历器对象ggen函数体外连续抛出两个错误。第一个错误被gen函数体内的catch语句捕获。g第二次抛出错误,由于gen函数内部的catch语句已经执行过了,不会再捕捉到这个错误了,所以这个错误就会被抛出gen函数体,被函数体外的catch语句捕获。

值得注意的是:

  • 如果Generator函数内部没有部署try...catch代码块,那么遍历器对象的throw方法抛出的错误,将被外部try...catch代码块捕获。
  • 如果Generator函数内部和外部都没有部署try...catch代码块,那么程序将报错,直接中断执行。

遍历器对象的throw方法被捕获以后,会附带执行一次.next()方法,代码执行会暂停到下一条yield表达式处。看下面这个例子:

function* gen(){ try { yield console.log('a'); } catch (e) { console.log(e); // 'Error' } yield console.log('b'); yield console.log('c'); } var g = gen(); g.next(); // 控制台输出:'a' g.throw('Error'); // 控制台输出:'b' // throw的错误被内部catch语句捕获, // 会自动在执行一次g.next() g.next(); // 控制台输出:'c'
Copy after login

5、Generator.prototype.return()

Generator函数返回的遍历器对象,还有一个.return()方法,可以返回给定的值,并且直接结束对遍历器对象的遍历。

function* gen() { yield 1; yield 2; yield 3; } var g = gen(); g.next(); // { value: 1, done: false } // 提前结束对g的遍历。尽管yield还没有执行完 // 此时done属性值为true,说明遍历结束 g.return('foo'); // { value: "foo", done: true } g.next(); // { value: undefined, done: true }
Copy after login

如果.return()方法调用时,不提供参数,则返回值的value属性为undefined

6、yield* 表达式

yield*用来在一个Generator函数里面执行另一个Generator函数。

如果在一个Generator函数内部,直接调用另一个Generator函数,默认情况下是没有效果的。

function* gen1() { yield 'a'; yield 'b'; } function* gen2() { yield 'x'; // 直接调用gen1() gen1(); yield 'y'; } // 遍历器对象可以使用for...of遍历所有状态 for (let v of gen2()){ 只输出了gen1的状态 console.log(v); // 'x' 'y' }
Copy after login

上面的例子中,gen1gen2都是Generator函数,在gen2里面直接调用gen1,是不会有效果的。

这个就需要用到yield*表达式。

function* gen1() { yield 'a'; yield 'b'; } function* gen2() { yield 'x'; // 用 yield* 调用gen1() yield* gen1(); yield 'y'; } for (let v of gen2()){ 输出了gen1、gen2的状态 console.log(v); // 'x' 'a' 'b' 'y' }
Copy after login

小节

This article mainly explains the basic syntax and some details of the Generator function, the definition of the Generator function, yield expression, .next() method and parameter passing, .throw() method, .return() method and yield* expression.

As mentioned at the beginning of the article, the Generator function is a solution to asynchronous programming proposed by ES6. In practical applications, the yield keyword is generally followed by an asynchronous operation. When the asynchronous operation returns successfully, the .next() method is called to hand over the asynchronous process to the next yield expression.

This article has ended here. For more other exciting content, you can pay attention to theJavaScript Video Tutorialcolumn on the PHP Chinese website!

The above is the detailed content of Introduction to the usage of Generator function in ES6. For more information, please follow other related articles on the PHP Chinese website!

Related labels:
source:segmentfault.com
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template
About us Disclaimer Sitemap
php.cn:Public welfare online PHP training,Help PHP learners grow quickly!