Generator函数的数据交换和错误处理。
Generator函数可以暂停执行和恢复执行,这是它能封装异步任务的根本原因。除此之外,它还有两个特性,使他可以作为异步编程的完整解决方案:函数体内的数据交换和错误处理机制。next返回值的value属性,是Generator函数向外输出数据,next方法还可以接受参数,向Generator函数体内输入数
function *g(x){
yield "leo";
var y= yield "lalalal"+x;
return y;
}
var p = g(1);
console.log(p.next());// { value: "leo", done: false }
console.log(p.next('donna'));//{ value: "lalalal1", done: false }
console.log(p.next("honery"));//{ value: "honery", done: true }
console.log(p.next("honery"));// { value: undefined, done: true }
Generator函数内部还可以部署错误处理代码,捕获函数体外抛出的错误
try {
var y = yield x + 2;
} catch (e){
console.log(e);
}
return y;
}
var g = gen(1);
console.log(g.next());//{ value: 3, done: false }
g.throw('出错了');
console.log(g.next(12));//{ value: undefined, done: true }
上面的代码,在Generator函数体外,使用指针对象的throw方法抛出的错误,可以被函数体内的try…catch代码块捕获。这意味着,出错的代码与处理错误的代码,在时间和空间上的分离。
再看一个例子
function* gen(x){
try {
var y = yield x + 2;
} catch (e){
console.log(e);
}
yield "donna";
return y;
}
var g = gen(1);
console.log(g.next());//{ value: 3, done: false }
console.log(g.throw('出错了'));//{ value: "donna", done: false }
console.log(g.next(12));//{ value: undefined, done: true }
我们之前说过,go.throw方法被捕获以后,自动执行了一次next方法,但从上面的两个例子中我们可以发现,go.throw方法只会自动执行yield语句,但并不自动执行return语句,这和调用next方法还是有区别的。当然,只要Generator函数内部部署了try…catch代码块,那么遍历器的throw方法抛出的错误,不影响下一次遍历,这也是从上面的代码中可以看到的。
之前忽略了next方法参数的作用,今天给补上
next方法的参数:
yield表达式本身没有返回值,或者说总是返回undefined。next方法可以带一个参数,该参数就会被当做上一个yield表达式的返回值。
注意,由于next方法的参数表示上一个yield表达式的返回值,所以,在第一次调用next方法时,传递的参数是无效的,只有第二次调用next方法,传递的参数才是有效的。从语法上讲,第一个next方法用于启动遍历器对象,所以不用带参数。
看个例子
function *g(x){
yield 2+x;
var y= 2*(yield 3+x);
yield y;
return y+x;
}
var p = g(1);
console.log(p.next());// { value: 3, done: false }
console.log(p.next(7));// { value: 4, done: false }
console.log(p.next(5));// { value: 10, done: false }
console.log(p.next(5));// { value: 11, done: false }
console.log(p.next(6));// { value: undefined, done: false }
切记:next方法返回的是yield表达式后面的值,next方法传递的参数不是x的值,而是上一个yield表达式的返回值(不是上一个整个表达式的值,知识yield后面的值)。所以上面的代码,第一次调用next方法时,返回的是2+x,x=1,所以就是3,第二次调用next方法,虽然传递了参数,但它代表的上一个yield表达式的值,而我们并没有使用这个值,第三次调用next方法的时候,传递的参数为4,说明上一个yield表达式3+x=5,所以y = 2*5=10,所以这一次yield的值为10,第四次调用next方法,由于y=10,x=1,所以返回的结果就是11.
function *g(x){
yield 2+x;
var y= 2*(yield 3+x);
yield y+1;
yield y+x;
}
var p = g(1);
console.log(p.next());// { value: 3, done: false }
console.log(p.next(7));// { value: 4, done: false }
console.log(p.next(5));// { value: 11, done: false }
console.log(p.next(5));// { value: 11, done: false }此处y=10,x=1
console.log(p.next(6));// { value: undefined, done: false }
function *g(x){
yield 2+x;
var y= 2*(yield 3+x);
var z = yield y+1;
yield z+x+y;
}
var p = g(1);
console.log(p.next());// { value: 3, done: false }
console.log(p.next(7));// { value: 4, done: false }
console.log(p.next(5));// { value: 11, done: false }
console.log(p.next(5));// { value: 16, done: false }此处z=5,y=10,x=1
console.log(p.next(6));// { value: undefined, done: false }
还有下面的情况
function *g(x){
yield 2+x;
var y= 2*(yield 3+x);
var z = yield 1;
yield z+x+y;
}
var p = g(1);
console.log(p.next());// { value: 3, done: false }
console.log(p.next(7));// { value: 4, done: false }
console.log(p.next(5));// { value: 1, done: false }
console.log(p.next(5));// { value: 16, done: false }
console.log(p.next(6));// { value: undefined, done: false }
function *g(x){
yield 2+x;
var y= 2*(yield 3+x);
var z = yield 1;
yield z+x+y;
}
var p = g(1);
console.log(p.next());// { value: 3, done: false }
console.log(p.next(7));// { value: 4, done: false }
console.log(p.next());// { value: 1, done: false }
console.log(p.next(5));// { value: NaN, done: false }
console.log(p.next(6));// { value: undefined, done: false }
做一个小小的总结:next方法返回一个对象,它的value属性就是当前yield表达式的值(此处暂且不讨论done属性的值),如果next方法在调用的时候传递了参数(不包括第一个调用next方法),那么这个参数就是上一个yield表达式的返回值,也可以说就是当上一个yield表达式整体(或yield整体运算后)赋值给了一个变量,而下一次调用next方法,上一个yield表达式的值就是本次next方法的参数,如果没有传参,则为undefined。(还有就是不要和next参数和Generator参数搞混喽)
这几个对比,每一个改动都很小,把我的疑惑的疑惑解决了,我自己总结了一下,可能有些说法不太科学,仅供参考(当然如果哪里说法不对,也希望您可以提出您的宝贵意见),实践才是检验真理的唯一标准,嗯、还是要多动手才是。