Generator函数
简介
基本概念
- 是一种异步编程解决方案
- 可以看成一个状态机,封装了多个内部状态。
- 执行Generator函数会返回一个遍历器对象
特征
- function命令与函数名之间有一个星号
- 函数体内部使用
yield
语句定义不同的内部状态
只有调用next方法且内部指针指向该语句时才会执行yield后面的表达式 yield
只能用在Generator函数中
var arr = [1, [[2, 3], 4], [5, 6]];
var flat = function* (a) {
// forEach的参数是普通函数,使用yield报错
a.forEach(function (item) {
if (typeof item !== 'number') {
yield* flat(item);
} else {
yield item;
}
});
};
for (var f of flat(arr)){
console.log(f);
}
与Iterator接口的关系
可以把Generator赋值给对象的Symbol.iterator属性,是对象具有Iterator接口,可以被...
遍历
var mine = {};
mine[Symbol.iterator] = function* () {
yield 1;
yield 11;
yield 111;
}
console.log([...mine]); //[ 1, 11, 111 ]
next方法参数
如果没有参数,每次运行到yield语句时,变量reset的值总是underfined
var reset = yield i;
注意:next方法的参数表示上一条yield语句的返回值
,所以第一次next方法的参数传递是无效的。(第一个next方法用来启动遍历器对象,所以不用带参数)
for…of循环
next方法返回对象的done属性为true,for…of循环就会终止,且不包含该返回对象
// return语句返回的done属性为true
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
通过Generator函数为对象加上遍历接口
function* objectEntries(obj) {
let propKeys = Reflect.ownKeys(obj); // 获取该对象所有键
for (const propKey of propKeys) {
yield [propKey, obj[propKey]]
}
}
let jane = { first: 'Jane', last: 'Doe'};
for (const [key, value] of objectEntries(jane)) {
console.log(`${key}:${value}`);
}
或者将Generator函数加到对象的Symbol.iterator属性
function* objecyEntries2() {
let propKeys = Object.keys(this);
for (const propKey of propKeys) {
yield [propKey, this[propKey]]
}
}
let janee = { first: 'Jane', last: 'Doe'};
janee[Symbol.iterator] = objecyEntries2;
for (const [key, value] of janee) {
console.log(`${key}:${value}`);
}
Generator.prototype.throw()
throw方法可以在函数体外抛出错误,然后在Generator函数体内捕获。
Generator.prototype.return()
返回给定的值,终结Generator函数的遍历。
yield*表达式
- 用来在一个Generator函数里面执行另一个Generator函数
/**
* yield*
*/
function* foo() {
yield 'a';
yield 'b';
}
function* bar() {
yield 'x';
yield* foo();
yield 'y';
}
for (const v of bar()) {
console.log(v); // x a b y
}
- yield* 遍历完全二叉树
作为对象属性的Generator函数
/**
* 作为对象属性的Generator函数
*/
let obj = {
* myGeneratorMethod() {
// your code
}
};
// 等价于
let obj = {
myGeneratorMethod: function* () {
// your code
}
}
Generator函数 this
Generator函数返回一个遍历器(Generator函数的实例),也继承了Generator函数的prototype对象上方法。
/**
* 遍历器obj是g的实例,继承了g.prototype
*/
function* g() {
g.prototype.hello = function() {
return 'hi';
};
let obj = g();
obj instanceof g //true
obj.hello() // 'hi'
}
如果把g当作普通的构造函数就不会生效,因为g返回的总是遍历器对象,而不是this对象
/**
* 如果把g当作普通的构造函数就不会生效,因为g返回的总是遍历器对象,而不是this对象
*/
function* g() {
this.a = 11;
}
let obj = g();
obj.a // undefined
获得this的方法:生成一个空对象,使用call方法绑定Generator函数内部的this,构造函数调用后空对象就是Generator函数的实例对象。
/**
* 获取this的方法
*/
function* F() {
this.a = 1;
yield this.b = 12;
}
var obj = {};
var f = F.call(obj);
obj.a // 1
obj.b // 12
含义
Generator与状态机
Generator实现少了用来保存状态的外部变量,更简洁、安全。
/**
* Generator实现状态机
*/
//状态机
var ticking = true;
var clock = function() {
if(ticking)
console.log('Tick');
else
console.log('Tock');
ticking = !ticking;
}
// Generator实现
var clock = function* () {
while (true) {
console.log('Tick');
yield;
console.log('Tock');
yield;
}
};
Generator与协程
- 协程与子例程的差异
- 协程与普通线程的差异
应用
Generator可以暂停函数执行,返回任意表达式的值。
异步操作的同步化表达
调用next方法时往后执行,不需要写回调函数。
/**
* 异步操作的同步化表达
*/
function* loadUI() {
showLoadingScreen();
yield loadUIDataAsynchronously();
hideLoadingScreen();
}
var loader = loadUI();
loader.next(); // 加载UI
loader.next(); // 卸载UI
实现AJAX
/**
* 通过Generator函数部署AJAX操作,可以用同步方式表达
*/
function* main() {
var result = yield request("http://yoururl");
var resp = JSON.parse(result);
console.log(resp.value);
}
function request(url) {
makeAjaxCall(url, function(response) {
it.next(response);
});
}
var it = main();
it.next();
控制流管理
/**
* 控制流管理
*/
// 多部操作
step1(function (value1) {
step2(value1, function (value2) {
step3(value2, function (value3) {
// do something...
});
});
});
// Promise
Promise.resolve(step1).then(step2).then(function (value3) {
// do something
}, function(err) {
// Handle any err
}).done();
// Generator(只适用同步操作)
function* longRunningTask(value1) {
try{
var value2 = yield step1(value1);
var value3 = yield step1(value2);
var value4 = yield step1(value3);
} catch(e) {
// Handle any err
}
}
部署Iterator接口
/**
* 利用Generator函数在任意对象上部署Iterator接口
*/
function* iterEntries(obj) {
let keys = Object.keys(obj);
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
yield [key, obj[key]];
}
}
let myObj = { foo: 3, bar: 7};
for (const [key, value] of iterEntries(myObj)) {
console.log([key, value]); // foo 3, bar 7
}
作为数据结构
Generator函数可以返回一系列的值,可以对任意表达式提供类似数组的接口
/**
* 作为数据结构
*/
function* doStuff() {
yield fstat.readFile.bind(null, 'hello.txt');
yield fstat.readFile.bind(null, 'wrold.txt');
yield fstat.readFile.bind(null, 'and-such.txt');
}
for (const task of doStuff()) {
// 调用task函数
}