generator
先看看什麽是generator
函数:
function* fib(max) { // 与普通函数区别1: *
var
t,
a = 0,
b = 1,
n = 0;
while (n < max) {
yield a; // 与普通函数区别2: 使用yield表达式
[a, b] = [b, a + b];
n ++;
}
return;
}
// 第一种方法执行
var f = fib(5); // 仅仅是创建了一个generator对象,还没有去执行它
// f.next()才开始真正运行,它的作用是会把yield后的值包装成固定对象返回
console.log(f.next()); // {value: 0, done: false}
console.log(f.next()); // {value: 1, done: false}
console.log(f.next()); // {value: 1, done: false}
console.log(f.next()); // {value: 2, done: false}
console.log(f.next()); // {value: 3 , done: false}
// 运行结束后,done值变为 true,返回的值变成 undefined
console.log(f.next()); // {value: undefined, done: true}
// 第二种方法执行
for (var x of fib(10))
console.log(x); // 依次输出0, 1, 1, 2, 3, ...
generator和普通函数相比,因为generator可以在执行过程中多次返回,所以它看上去就像一个可以记住执行状态的函数。
yield
代码里边有个 yield
,我们来看看它是如何工作使用的:
Generator
函数返回的Iterator
运行的过程中,如果遇到了yield就会把yield后边的值返回,然后函数暂停执行直到下一个next()
方法开始执行,函数才会从上次暂停的位置重新开始。yield
可以和return
一起使用,也可以不使用return
- 当
yield
和return
一起使用时,return
的值会作为最后返回的值,如果有yield
出现在return
的后面,那部分就是失效的。 yield
只能在generator
函数的场景下使用yield*
在Generator
函数嵌套时使用,例子如下:
function* foo() {
yield 0;
yield 1;
}
function* bar() {
yield 'x';
yield* foo(); // yield* 函数
yield 'y';
}
for (let v of bar()){
console.log(v);
};
next()
- 执行
next()
方法后得到的返回值结构为:
{
value : ... ,
done : false // done的值为一个布尔值, 如果Interator未遍历完毕, 他会返回false, 否则返回true;
}
next()
方法可以设置参数,这个参数会当作上个yield
语句的返回值next()
的参数在异步处理中非常重要,因为这种时候需要上一个异步的结果
function *foo2(x) {
var y = 2 * (yield (x + 1));
var z = yield (y / 3);
return (x + y + z);
}
var it2 = foo2( 5 );
// 异步情况下若有参数
console.log( it2.next() ); // { value:6, done:false }
console.log( it2.next(12) ); // { value:8, done:false } // 相当于从 var y = 2 * 12 开始执行
console.log( it2.next(13) ); // { value:42, done:true }
var it3 = foo2( 5 );
console.log( it3.next() ); // { value:6, done:false }
console.log( it3.next() ); // { value:NaN, done:false } // 无参数时,相当于var y = 2 * NaN
console.log( it3.next() ); // { value:NaN, done:true }
Generator函数与 throw()
-
先复习一下有关
try-catch
和throw
的相关知识:- try 语句允许您定义一个代码块,以便在执行时检测错误。
- catch 语句允许你定义一个要执行的代码块,如果 try 代码块中发生错误。
try { 供测试的代码块 } catch(err) { 处理错误的代码块 }
- throw 语句允许创建自定义错误并抛出它
- 因此如果把 throw 与 try 和 catch 一同使用,就可以控制程序流并生成自定义错误消息。例如:
x = document.getElementById("demo").value; try { if(x == "") throw "空的"; if(isNaN(x)) throw "不是数字"; x = Number(x); if(x < 5) throw "太小"; if(x > 10) throw "太大"; } catch(err) { message.innerHTML = "输入是 " + err; }
-
如果Generator函数内包含try-catch和throw,当yield表达式在try内部, 那么如果执行Generator生成器的throw()方法时,这个错误会被内部的try{}catch(){}捕获
var g = function* () {
while(true) {
try {
yield;
} catch (e) {
console.log('内部捕获', e);
}
}
};
var i = g();
i.next();
try {
i.throw('a');
i.throw('b');
} catch (e) {
console.log('外部捕获', e);
}
// 内部捕获 a
// 内部捕获 b
return()
如果执行Iterator的return()方法, 那么这个迭代器的返回会被强制设置为迭代完毕
function* gen() {
yield 0;
yield 1;
yield 2;
yield 3;
};
let g = gen();
console.log(g.return("heheda")); //输出:{ value: 'heheda', done: true }
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); // 当捕获到异常时catch发挥作用时,
// 后续的代码就不再执行下去
r2 = yield ajax('http://url-2', data2);
r3 = yield ajax('http://url-3', data3);
success(r3);
}
catch (err) {
handle(err);
}
使用小例子
生成一个自增的ID,编写一个next_id()
函数(不得使用闭包和全局变量)
function* next_id() {
var num = 0;
while(true){
yield num++;
}
}