生成器

function foo(x) { return x + x; }
var r = foo(1); // 调用foo函数

函数在执行过程中,如果没有遇到return语句(函数末尾如果没有return,就是隐含的return undefined;),控制权无法交回被调用的代码。


generator跟函数很像,定义如下:
~~~
function* foo(x) {
yield x + 1;
yield x + 2;
return x + 3;
}

generator和函数不同的是,
`generator由function*定义(注意多出的*号)`
并且,除了return语句,还可以用yield返回多次。

generator就是能够返回多次的“函数”?返回多次有啥用?


我们以一个著名的斐波那契数列为例,它由0,1开头:
0 1 1 2 3 5 8 13 21 34 ...
~~~
function fib(max) {
var
t,
a=0,
b=1;
arr=\[0,1];

while (arr.length \< max) {
t=a+b;
a=b;
b=t;
arr.push(t);
}

return art;;
}


函数只能返回一次. 所以必须返回一个 array.
~~~
function* fib(max) {
var
t,
a = 0,
b = 1,
n = 1;
while (n < max) {
yield a;
t = a + b;
a = b;
b = t;
n ++;
}
return a;
}


generator 函数的调用和 普通函数不一样.
`fib(5); // fib {[[GeneratorStatus]]: "suspended", [[GeneratorReceiver]]: Window}`
fib(5)仅仅是创建了一个generator对象,还没有去执行它。

要调用 generator对象有两个方法. 
1. 不断的调用 generator 对象的 next() 方法.
~~~
var f = fib(5);
f.next(); // {value: 0, done: false}
f.next(); // {value: 1, done: false}
f.next(); // {value: 1, done: false}
f.next(); // {value: 2, done: false}
f.next(); // {value: 3, done: true}


直接用for ... of循环迭代generator对象,这种方式不需要我们自己判断done:
~~~
for (var x of fib(5)) {
console.log(x); // 依次输出0, 1, 1, 2, 3
}



*generator还有另一个巨大的好处,就是把异步回调代码变成“同步”代码。这个好处要等到后面学了AJAX以后才能体会到。*



~~~
ajax('http://url-1', data1, function (err, result) {
if (err) {
return handle(err);
}
ajax('http://url-2', data2, function (err, result) {
if (err) {
return handle(err);
}
ajax('http://url-3', data3, function (err, result) {
if (err) {
return handle(err);
}
return success(result);
});
});
});

回调越多,代码越难看。

有了generator的美好时代,用AJAX时可以这么写:
\~\~\~
try {
r1 = yield ajax('http://url-1', data1);
r2 = yield ajax('http://url-2', data2);
r3 = yield ajax('http://url-3', data3);
success(r3);
}
catch (err) {
handle(err);
}
看上去是同步的代码,实际执行是异步的。