导航
导航
文章目录
  1. Generator函数
    1. 一、yield 表达式
    2. 二、for…of 循环
    3. 三、作为对象属性的Generator函数
    4. 四、应用

读书笔记-es6Generator函数

Generator函数

从语法上,可以把它理解成,Generator 函数是一个状态机,封装了多个内部状态。
调用 Generator 函数后,该函数并不执行,返回的也不是函数运行结果,而是一个指向内部状态的指针对象(遍历器对象)。
Generator 函数是分段执行的,yield表达式是暂停执行的标记,而next方法可以恢复执行。
Generator 函数也不能跟new命令一起用,会报错。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function* helloWorldGenerator() {
yield 'hello';
yield 'world';
return 'ending';
}
var hw = helloWorldGenerator();
hw.next()
// { value: 'hello', done: false }
hw.next()
// { value: 'world', done: false }
hw.next()
// { value: 'ending', done: true }
hw.next()
// { value: undefined, done: true }

总结一下,调用 Generator 函数,返回一个遍历器对象,代表 Generator 函数的内部指针。以后,每次调用遍历器对象的next方法,就会返回一个有着value和done两个属性的对象。value属性表示当前的内部状态的值,是yield表达式后面那个表达式的值;done属性是一个布尔值,表示是否遍历结束。

写法

1
2
3
4
function * foo(x, y) { ··· }
function *foo(x, y) { ··· }
function* foo(x, y) { ··· }
function*foo(x, y) { ··· }

一、yield 表达式

由于 Generator 函数返回的遍历器对象,只有调用next方法才会遍历下一个内部状态,所以其实提供了一种可以暂停执行的函数。yield表达式就是暂停标志。

遍历器对象的next方法的运行逻辑如下:
1)遇到yield表达式,就暂停执行后面的操作,并将紧跟在yield后面的那个表达式的值,作为返回的对象的value属性值。
2)下一次调用next方法时,再继续往下执行,直到遇到下一个yield表达式。
3)如果没有再遇到新的yield表达式,就一直运行到函数结束,直到return语句为止,并将return语句后面的表达式的值,作为返回的对象的value属性值。
4)如果该函数没有return语句,则返回的对象的value属性值为undefined。

1
2
3
4
5
6
function* gen() {
yield 123 + 456;
}
var g = gen()
gen.next()
//{ value: 579, done: false }

二、for…of 循环

for…of循环可以自动遍历 Generator 函数时生成的Iterator对象,且此时不再需要调用next方法。

1
2
3
4
5
6
7
8
9
10
11
12
function *foo() {
yield 1;
yield 2;
yield 3;
yield 4;
yield 5;
return 6;
}
for (let v of foo()) {
console.log(v);
}
// 1 2 3 4 5

这里需要注意,一旦next方法的返回对象的done属性为true,for…of循环就会中止,且不包含该返回对象,所以上面代码的return语句返回的6,不包括在for…of循环之中。

除了for…of循环以外,扩展运算符(…)、解构赋值和Array.from方法内部调用的,都是遍历器接口。这意味着,它们都可以将 Generator 函数返回的 Iterator 对象,作为参数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function* numbers () {
yield 1
yield 2
return 3
yield 4
}
// 扩展运算符
[...numbers()] // [1, 2]
// Array.from 方法
Array.from(numbers()) // [1, 2]
// 解构赋值
let [x, y] = numbers();
x // 1
y // 2
// for...of 循环
for (let n of numbers()) {
console.log(n)
}
// 1
// 2

三、作为对象属性的Generator函数

1
2
3
4
5
6
7
8
9
10
11
let obj = {
* myGeneratorMethod() {
···
}
};
//等同于
let obj = {
myGeneratorMethod: function* () {
// ···
}
};

四、应用

1)异步操作的同步化表达

1
2
3
4
5
6
7
8
9
10
function* loadUI() {
showLoadingScreen();
yield loadUIDataAsynchronously();
hideLoadingScreen();
}
var loader = loadUI();
// 加载UI
loader.next()
// 卸载UI
loader.next()
1
2
3
4
5
6
7
8
9
10
11
12
function* main() {
var result = yield request("http://some.url");
var resp = JSON.parse(result);
console.log(resp.value);
}
function request(url) {
makeAjaxCall(url, function(response){
it.next(response);
});
}
var it = main();
it.next();

2)控制流管理

1
2
3
4
5
6
7
8
9
step1(function (value1) {
step2(value1, function(value2) {
step3(value2, function(value3) {
step4(value3, function(value4) {
// Do something with value4
});
});
});
});

采用 Promise 改写上面的代码。

1
2
3
4
5
6
7
8
9
10
Promise.resolve(step1)
.then(step2)
.then(step3)
.then(step4)
.then(function (value4) {
// Do something with value4
}, function (error) {
// Handle any error from step1 through step4
})
.done();

Generator 函数可以进一步改善代码运行流程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function* longRunningTask(value1) {
try {
var value2 = yield step1(value1);
var value3 = yield step2(value2);
var value4 = yield step3(value3);
var value5 = yield step4(value4);
// Do something with value4
} catch (e) {
// Handle any error from step1 through step4
}
}
scheduler(longRunningTask(initialValue));
function scheduler(task) {
var taskObj = task.next(task.value);
// 如果Generator函数未结束,就继续调用
if (!taskObj.done) {
task.value = taskObj.value
scheduler(task);
}
}