Js 中的生成器函数
·
Yin灏
function* fn() {
console.log(1);
//暂停!
yield;
//调用next方法继续执行
console.log(2);
}
var iter = fn();
iter.next(); //1
iter.next(); //2
-
函数生成器特点是函数名前面有一个
*
-
通过调用函数生成一个控制器
-
调用 next() 方法开始执行函数,生成器函数的执行,从调用 next() 函数开始
-
遇到 yield 函数将暂停
-
再次调用 next() 继续执行函数
消息传递
除了暂停和继续执行外,生成器同时支持传值。
function* fn() {
var a = yield 'hello';
yield;
console.log(a);
}
var iter = fn();
var res = iter.next();
console.log(res.value); //hello
iter.next(2); // 这里传值了 a = 2,如果不传值 a = undefined
iter.next(); //2
可以看到,yield 后面有一个字符串,在第一次调用 next 时,暂停在这里且返回给了 iter.next()
。
而暂停的地方是一个赋值语句,需要一个变量给 a,于是 next() 方法中传了一个参数 2 替换了 yield,最后打印 a 得到了 2。
异步应用
通过 yield 来实现异步控制流程:
function fn(a, b) {
//假设这是一个ajax请求
ajax('url' + a + b, function(data) {
//数据请求到会执行it.next
it.next(data);
});
}
//这里是函数生成器
function* g() {
//当异步操作完毕 yield 会得到值
//这里会自动继续执行
var text = yield fn(a, b);
console.log(text);
}
var it = g();
it.next();
yield + promise
function foo(x) {
// return 和 yield 作用相同,将返回值添加到了 { value: 返回值, done:false }
return request('url' + x);
}
//等待promise决议值返回
function* fn() {
var text = yield foo(1);
}
var it = fn();
//返回一个promise
var p = it.next().value;
//对promise处理
p.then(function(text) {
//这里继续执行生成器
it.next(text);
})
封装
可以将上面的 yield+promise 进行封装,得到下面的函数:
function run(gen) {
//获取除了生成器本身以外的参数
var args = [].slice.call(arguments, 1),
it;
//it = main()
it = gen.apply(this, args);
return Promise.resolve().then(function handleNext(value) {
//第一次启动无value
var next = it.next(value);
return (function handleResult(next) {
//执行完毕返回
if (next.done) {
return next.value;
} else {
//如果还有就决议next.value传给handleNext
return Promise.resolve(next.value).then(handleNext, function(err) {});
}
})(next);
});
}
//这是一个函数生成器
function* main() {
//...
};
//该调用会自动异步运行直到结束
run(main);
如果有两个异步操作,获取到返回的两个数据后,再进行第三个异步操作,可以这么做:
function foo() {
var p1 = request('url1'),
p2 = request('url2');
//每一个request异步请求完成后会自动解除yield
var r1 = yield p1,
r2 = yield p2;
var r3 = yield request('url3' + r1 + r2);
console.log(r3);
}