一、简介
Generator 函数是 ES6 提供的一种异步编程
解决方案,语法行为与传统函数完全不同。
Generator 函数有多种理解角度。语法上,首先可以把它理解成,Generator 函数是一个状态机,封装了多个内部状态。
执行 Generator 函数会返回一个遍历器对象,也就是说,Generator 函数除了状态机,还是一个遍历器对象生成函数。返回的遍历器对象,可以依次遍历 Generator 函数内部的每一个状态。
二、基本用法
function * foo(x, y) { ··· }
function *foo(x, y) { ··· }
function* foo(x, y) { ··· }
function*foo(x, y) { ··· }
由于 Generator 函数仍然是普通函数,所以一般的写法是上面的第三种,即星号紧跟在 function 关键字后面。本书也采用这种写法。
该函数并不执行,返回的也不是函数运行结果,而是一个指向内部状态的指针对象。
function* helloWorldGenerator() {
yield 'hello';
yield 'world';
return 'ending';
}
var hw = helloWorldGenerator();
console.log(hw);
console.log(hw.next());
console.log(hw.next());
console.log(hw.next());
console.log(hw.next());
helloWorldGenerator返回的是一个指针对象,可以调用 next 方法,内部指针就从函数头部或上一次停下来的地方开始执行,直到遇到下一个 yield 表达式(或 return 语句)为止。
换言之,Generator 函数是分段执行的, yield 表达式是暂停执行的标记,而 next 方法可以恢复执行。
三、next传参
yield表达式本身没有返回值,或者说总是返回 undefined 。
看一个例子:
function* add(x, y){
let z = yield x + y;
console.log('上一个yield的结果z是:', z);
return z + 1;
}
const f = add(1, 2);
console.log(f.next());
console.log(f.next());
console.log(f.next());
可以看到,使用z来接收yield(x, y)的结果,发现是undefine。
next 方法可以带一个参数,该参数就会被当作上一个 yield 表达式的返回值
。
我们使用next带参数解决上面undefine问题。
function* add(x, y){
let z = yield x + y;
console.log('上一个yield的结果z是:', z);
return z + 1;
}
const f = add(1, 2);
let z = f.next();
console.log(z);
console.log(f.next(z.value));
console.log(f.next());
四、Generator函数异步应用
ES6 诞生以前,异步编程的方法,大概有下面四种。
- 回调函数
- 事件监听
- 发布/订阅
- Promise 对象
Generator 函数将 JavaScript 异步编程带入了一个全新的阶段。
var fetch = require('node-fetch');
function* gen(){
var url = 'https://api.github.com/users/github';
var result = yield fetch(url);
console.log(result.bio);
}
var g = gen();
var result = g.next();
//result.value是一个promise实例
result.value.then(function(data){
return data.json();
}).then(function(data){
//继续执行generator函数,传入上次结果data
g.next(data);
});
而es2017提出的async函数
则是 Generator 函数的语法糖,使用async配合await使得异步操作变得更加方便。