您的位置:首页 > 其它

es6的Generator函数

2016-04-17 17:36 375 查看

摘自Generator 函数

Generator
函数是协程在ES6的实现,最大特点就是可以交出函数的执行权(即暂停执行)。

整个Generator函数就是一个封装的异步任务,或者说是异步任务的容器。异步操作需要暂停的地方,都用yield语句注明。

Generator
函数有多种理解角度。从语法上,首先可以把它理解成,Generator函数是一个状态机,封装了多个内部状态。

function* helloWorldGenerator() {
yield 'hello';
yield 'world';
return 'ending';
}

var hello = helloWorldGenerator();
hello.next();// {value: "hello", done: false}
hello.next();// {value: "world", done: false}
hello.next();// {value: "ending", done: true}
hello.next();// {value: undefined, done: true}


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

next
方法的参数

yield
句本身没有返回值,或者说总是返回
undefined
next
方法可以带一个参数,该参数就会被当作上一个
yield
语句的返回值。

function* gen(){
var x = yield "a";
var y = yield "b";
return x + y;
}

var g = gen();
g.next(); // {value: "a", done: false}
g.next("hello"); // {value: "b", done: false}
g.next("world"); // {value: "helloworld", done: true}


第一个
next
返回
yield "a";
的value值
a
,第二个
next
返回
yield "b";
的value值”b”。

但是
next
接收参数
hello
作为上一个
yield "a"
语句的返回值,
x
的值为
hello
.

第三个
next
接收参数
world
作为上一个
yield "b"
语句的返回值,
y
的值为
world
.
return
返回的值就是
helloworld


var g = gen();
g.next();//{value: "a", done: false}
g.next();//{value: "b", done: false}

//`next`方法没有传入参数时,`yield`语句返回`undefined`. undefined + undefined => NaN
g.next();//{value: "NaN", done: true}


第一个next方法用来执行完第一个
yield
语句,所以不用带有参数。

如果想要第一次调用next方法时,就能够输入值,可以在Generator函数外面再包一层。

function wrapper(generatorFunction) {
return function (...args) {
var generatorObject = generatorFunction(...args);
generatorObject.next();
return generatorObject;
};
}

const wrapped = wrapper(function* () {
var result = yield "hello";
return result;
});

wrapped().next('world');


yield*
语句

如果在Generator函数内部,调用另一个Generator函数,默认情况下时没有效果的。

"use strict"
function* foo(){
yield "a";
yield "b";
}
function* bar(){
yield "x";
foo();
yield "y";
}
for (let v of bar()){
console.log(v);
}


上面代码中,
foo
bar
都是Generator函数,在
bar
里面调用
foo
,是没有效果的

这个就需要用到
yield*
语句,用来在一个Generator函数里面执行另一个Generator函数。

function* bar() {
yield 'x';
yield* foo();
yield 'y';
}

// 等同于
function* bar() {
yield 'x';
yield 'a';
yield 'b';
yield 'y';
}

// 等同于
function* bar() {
yield 'x';
for (let v of foo()) {
yield v;
}
yield 'y';
}

for (let v of bar()){
console.log(v);
}


yield*
不过是
for...of
的一种简写形式,完全可以用后者替代前者。

任何数据结构只要有
Iterator
接口,就可以被
yield*
遍历。

function* foo() {
yield 'hello';
yield* 'hello';
}
for (let v of foo()){
console.log(v);
}


如果被代理的Generator函数有
return
语句,那么可以向代理它的Generator函数返回数据。

function* foo(){
yield 2;
yield 3;
return "foo";
}
function* bar(){
yield 1;
var v = yield *foo();
console.log("v:",v);
yield 4;
}
for (let v of bar()){
console.log(v);
}


作为对象属性的
Generator
函数

let obj = {
* myGeneratorMethod(){
yield 1;
}
}


上面代码中,
myGeneratorMethod
属性前面有一个星号,表示这个属性是一个
Generator
函数

它的完整形式如下,与上面的写法是等价的。

let obj = {
myGeneratorMethod:function*(){
yield 1;
}
}


Generator函数的this

Generator函数总是返回一个遍历器,ES6规定这个遍历器是Generator函数的实例,也继承了Geenraotr函数的
prototype
对象上的方法。

function* g() {}

g.prototype.hello = function () {
return 'hi!';
};

let obj = g();

obj instanceof g; // true
obj.hello(); // 'hi!'


上面代码表明,
Generator
函数g返回的遍历器
obj
,是
g
的实例,而且继承了
g.prototype


但是,如果把
g
当作普通的构造函数,并不会生效,因为
g
返回的总是遍历器对象,而不是
this
对象。

function* g(){
this.a = 11;
}
let obj = g();
obj.a;//undefined


Generator与状态机

Generator是实现状态机的最佳结构。下面的clock函数就是一个状态机。

var ticking = true;
var clock = function(){
if(ticking) console.log("Tick!");
else console.log("Tock!");
ticking = !ticking;
}


上述代码的clock函数一共有两种状态(Tick和Tock),每运行一次,就改变一次状态。

var clock = function*(){
while(true){
console.log("Tick!");
yield;
console.log("Tock!");
yield;
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: