mirror of
https://github.com/ruanyf/es6tutorial.git
synced 2025-05-29 13:52:22 +00:00
修改generator
This commit is contained in:
parent
4cf70a17ab
commit
e635b182f6
@ -2,15 +2,15 @@
|
|||||||
|
|
||||||
## 含义
|
## 含义
|
||||||
|
|
||||||
所谓Generator,简单说,就是一个内部状态的遍历器,即每调用一次遍历器,内部状态发生一次改变(可以理解成发生某些事件)。ES6引入Generator函数,作用就是可以完全控制内部状态的变化,依次遍历这些状态。
|
所谓Generator,有多种理解角度。首先,可以把它理解成一个内部状态的遍历器,即每调用一次遍历器,内部状态发生一次改变(可以理解成发生某些事件)。ES6引入Generator函数,作用就是可以完全控制内部状态的变化,依次遍历这些状态。
|
||||||
|
|
||||||
Generator函数就是普通函数,但是有两个特征。一是,function关键字后面有一个星号;二是,函数体内部使用yield语句,定义遍历器的每个成员,即不同的内部状态(yield语句在英语里的意思就是“产出”)。
|
在形式上,Generator是一个普通函数,但是有两个特征。一是,function命令与函数名之间有一个星号;二是,函数体内部使用yield语句,定义遍历器的每个成员,即不同的内部状态(yield语句在英语里的意思就是“产出”)。
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
|
|
||||||
function* helloWorldGenerator() {
|
function* helloWorldGenerator() {
|
||||||
yield 'hello';
|
yield 'hello';
|
||||||
yield 'world';
|
yield 'world';
|
||||||
return 'ending';
|
return 'ending';
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,9 +50,7 @@ hw.next()
|
|||||||
|
|
||||||
总结一下,Generator函数使用iterator接口,每次调用next方法的返回值,就是一个标准的iterator返回值:有着value和done两个属性的对象。其中,value是yield语句后面那个表达式的值,done是一个布尔值,表示是否遍历结束。
|
总结一下,Generator函数使用iterator接口,每次调用next方法的返回值,就是一个标准的iterator返回值:有着value和done两个属性的对象。其中,value是yield语句后面那个表达式的值,done是一个布尔值,表示是否遍历结束。
|
||||||
|
|
||||||
Generator函数的本质,其实是提供一种可以暂停执行的函数。yield语句就是暂停标志,next方法遇到yield,就会暂停执行后面的操作,并将紧跟在yield后面的那个表达式的值,作为返回对象的value属性的值。当下一次调用next方法时,再继续往下执行,直到遇到下一个yield语句。如果没有再遇到新的yield语句,就一直运行到函数结束,将return语句后面的表达式的值,作为value属性的值,如果该函数没有return语句,则value属性的值为undefined。
|
由于Generator函数返回的遍历器,只有调用next方法才会遍历下一个成员,所以其实提供了一种可以暂停执行的函数。yield语句就是暂停标志,next方法遇到yield,就会暂停执行后面的操作,并将紧跟在yield后面的那个表达式的值,作为返回对象的value属性的值。当下一次调用next方法时,再继续往下执行,直到遇到下一个yield语句。如果没有再遇到新的yield语句,就一直运行到函数结束,将return语句后面的表达式的值,作为value属性的值,如果该函数没有return语句,则value属性的值为undefined。另一方面,由于yield后面的表达式,直到调用next方法时才会执行,因此等于为JavaScript提供了手动的“惰性求值”(Lazy Evaluation)的语法功能。
|
||||||
|
|
||||||
由于yield后面的表达式,直到调用next方法时才会执行,因此等于为JavaScript提供了手动的“惰性求值”(Lazy Evaluation)的语法功能。
|
|
||||||
|
|
||||||
yield语句与return语句有点像,都能返回紧跟在语句后面的那个表达式的值。区别在于每次遇到yield,函数暂停执行,下一次再从该位置继续向后执行,而return语句不具备位置记忆的功能。
|
yield语句与return语句有点像,都能返回紧跟在语句后面的那个表达式的值。区别在于每次遇到yield,函数暂停执行,下一次再从该位置继续向后执行,而return语句不具备位置记忆的功能。
|
||||||
|
|
||||||
@ -72,7 +70,7 @@ setTimeout(function () {
|
|||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
上面代码中,只有调用next方法时,函数f才会执行。
|
上面代码中,函数f如果是普通函数,在为generator变量赋值时就会执行。但是,函数f是一个Generator函数,就变成只有调用next方法时,函数f才会执行。
|
||||||
|
|
||||||
利用Generator函数,可以在任意对象上部署iterator接口。
|
利用Generator函数,可以在任意对象上部署iterator接口。
|
||||||
|
|
||||||
@ -97,7 +95,7 @@ for (let [key, value] of iterEntries(myObj)) {
|
|||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
上述代码中,由于Generator函数返回一个具有iterator接口的对象,所以只要让yield语句每次返回一个参数对象的成员,就可以在任意对象上部署next方法。
|
上述代码中,myObj是一个普通对象,通过iterEntries函数,就有了iterator接口。也就是说,可以在任意对象上部署next方法。
|
||||||
|
|
||||||
## next方法的参数
|
## next方法的参数
|
||||||
|
|
||||||
@ -234,8 +232,8 @@ function scheduler(task) {
|
|||||||
setTimeout(function () {
|
setTimeout(function () {
|
||||||
if (!task.next().done) {
|
if (!task.next().done) {
|
||||||
scheduler(task);
|
scheduler(task);
|
||||||
}
|
}
|
||||||
}, 0);
|
}, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
@ -247,13 +245,13 @@ function scheduler(task) {
|
|||||||
var Q = require('q');
|
var Q = require('q');
|
||||||
|
|
||||||
function delay(milliseconds) {
|
function delay(milliseconds) {
|
||||||
var deferred = Q.defer();
|
var deferred = Q.defer();
|
||||||
setTimeout(deferred.resolve, milliseconds);
|
setTimeout(deferred.resolve, milliseconds);
|
||||||
return deferred.promise;
|
return deferred.promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
function* f(){
|
function* f(){
|
||||||
yield delay(100);
|
yield delay(100);
|
||||||
};
|
};
|
||||||
|
|
||||||
```
|
```
|
||||||
@ -267,16 +265,16 @@ for...of循环可以自动遍历Generator函数,且此时不再需要调用nex
|
|||||||
```javascript
|
```javascript
|
||||||
|
|
||||||
function *foo() {
|
function *foo() {
|
||||||
yield 1;
|
yield 1;
|
||||||
yield 2;
|
yield 2;
|
||||||
yield 3;
|
yield 3;
|
||||||
yield 4;
|
yield 4;
|
||||||
yield 5;
|
yield 5;
|
||||||
return 6;
|
return 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let v of foo()) {
|
for (let v of foo()) {
|
||||||
console.log(v);
|
console.log(v);
|
||||||
}
|
}
|
||||||
// 1 2 3 4 5
|
// 1 2 3 4 5
|
||||||
|
|
||||||
@ -305,6 +303,52 @@ for (let n of fibonacci()) {
|
|||||||
|
|
||||||
从上面代码可见,使用for...of语句时不需要使用next方法。
|
从上面代码可见,使用for...of语句时不需要使用next方法。
|
||||||
|
|
||||||
|
## 作为数据结构的Generator
|
||||||
|
|
||||||
|
Generator可以暂停函数执行,返回任意表达式的值。这种特点使得Generator有多种应用场景。
|
||||||
|
|
||||||
|
- 异步操作的同步化表达(abstractions of async behavior)
|
||||||
|
- 控制流管理(control flow management)
|
||||||
|
- 数据结构(data structure)
|
||||||
|
|
||||||
|
第一种和第二种应用在本章前面部分,已经有所提及了。这里主要再补充一下,Generator可以看作是数据结构,因为它可以对任意表达式,提供类似数组的接口。
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
|
||||||
|
function *doStuff() {
|
||||||
|
yield fs.readFile.bind(null, 'hello.txt');
|
||||||
|
yield fs.readFile.bind(null, 'world.txt');
|
||||||
|
yield fs.readFile.bind(null, 'and-such.txt');
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
上面代码就是依次返回三个函数,但是由于使用了Generator函数,导致可以像处理数组那样,处理这三个返回的函数。
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
|
||||||
|
for (task of doStuff()) {
|
||||||
|
// task是一个函数,可以像回调函数那样使用它
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
实际上,如果用ES5表达,完全可以用数组模拟Generator的这种用法。
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
|
||||||
|
function doStuff() {
|
||||||
|
return [
|
||||||
|
fs.readFile.bind(null, 'hello.txt'),
|
||||||
|
fs.readFile.bind(null, 'world.txt'),
|
||||||
|
fs.readFile.bind(null, 'and-such.txt')
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
上面的函数,可以用一模一样的for...of循环处理!两相一比较,就不难看出Generator的这种用法,就是让数据或者操作,可以像数组那样处理。
|
||||||
|
|
||||||
## throw方法
|
## throw方法
|
||||||
|
|
||||||
Generator函数还有一个特点,它可以在函数体外抛出错误,然后在函数体内捕获。
|
Generator函数还有一个特点,它可以在函数体外抛出错误,然后在函数体内捕获。
|
||||||
|
@ -45,6 +45,7 @@
|
|||||||
- StackOverflow, [ES6 yield : what happens to the arguments of the first call next()?](http://stackoverflow.com/questions/20977379/es6-yield-what-happens-to-the-arguments-of-the-first-call-next): 第一次使用next方法时不能带有参数
|
- StackOverflow, [ES6 yield : what happens to the arguments of the first call next()?](http://stackoverflow.com/questions/20977379/es6-yield-what-happens-to-the-arguments-of-the-first-call-next): 第一次使用next方法时不能带有参数
|
||||||
- Kyle Simpson, [ES6 Generators: Complete Series](http://davidwalsh.name/es6-generators): 由浅入深探讨Generator的系列文章,共四篇
|
- Kyle Simpson, [ES6 Generators: Complete Series](http://davidwalsh.name/es6-generators): 由浅入深探讨Generator的系列文章,共四篇
|
||||||
- Gajus Kuizinas, [The Definitive Guide to the JavaScript Generators](http://gajus.com/blog/2/the-definetive-guide-to-the-javascript-generators): 对Generator的综合介绍
|
- Gajus Kuizinas, [The Definitive Guide to the JavaScript Generators](http://gajus.com/blog/2/the-definetive-guide-to-the-javascript-generators): 对Generator的综合介绍
|
||||||
|
- Jan Krems, [Generators Are Like Arrays](https://gist.github.com/jkrems/04a2b34fb9893e4c2b5c): 讨论Generator可以被当作数据结构看待
|
||||||
|
|
||||||
## Promise对象
|
## Promise对象
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user