Generator 函数的三个方法

目录

next 方法参数

Generator.prototype.throw()

Generator.prototype.return()

next()、throw()、return() 的共同点


next 方法参数

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

function* f() {
    for (var i = 0; true; i++) {
        var reset = yield i;
        if (reset) {
            i = -1;
            console.log(i);
        }
    }
}
let g = f();
console.log(g.next());
console.log(g.next());
console.log(g.next());
console.log(g.next(true));
console.log(g.next());

// {value: 0, done: false}
// {value: 1, done: false}
// {value: 2, done: false}
// -1
// {value: 0, done: false}
// {value: 1, done: false}
function* foo(x) {
  var y = 2 * (yield (x + 1));
  var z = yield (y / 3);
  return (x + y + z);
}

var a = foo(5);
a.next() // Object{value:6, done:false}
a.next() // Object{value:NaN, done:false}
a.next() // Object{value:NaN, done:true}

var b = foo(5);
b.next()   // { value:6, done:false }
b.next(12) // { value:8, done:false }
b.next(13) // { value:42, done:true }

解读: 

a.next() => x + 1 => 5 + 1 => 6

a.next() => y = 2 * (yield(...)),因为 next 没有参数,yield 表达式返回为 undefined => y = 2 * undefined => y = undefined => (y / 3) => (undefined / 3) => undefined

a.next() => z = yield(...) => z = undefined;(x + y + z) => (5 + undefined + undefined) => undefined

b.next() => x + 1 => 5 + 1 => 6

b.next(12) => y = 2 * (yield(...)),next 有参数,则上一个 yield 表达式返回值为 12 => y = 2 * 12 => y = 24 => (y / 3) = > (24 / 3) => 8

b.next(13) => z = 13 => x + y + z => 5 + 24 + 13 => 42

注意,由于 next 方法的参数代表上一个 yield 表达式的返回值,所以在第一次调用 next 是,传参是没有意义的。V8 引擎直接忽略第一次调用 next 时传递的参数,只有从第二次调用 next 传参才有效。

Generator.prototype.throw()

Generator 函数返回的遍历器对象,都有一个 throw 方法,可以在函数体外抛出异常,在函数体内捕获。

var g = function* () {
  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

如果不调用一次 i.next(),则只会输出 “外部捕获 a”;官方解释:因为第一次调用 next 方法,等同于启动执行 Generator 函数内部代码,否则 Generator 函数还没有开始执行,这是 throw 方法抛出的异常只可能抛出在函数体外部。(个人理解:不调用一次 next 方法,遍历器指针指向函数体头部,还未进入 Generator 函数 try catch 模块,所以未能在函数体内部捕获异常)

throw 方法可以接受一个参数,该参数会被 catch 语句接收。

遍历器对象的 throw 和全局的 throw 方法是不一样的。如果上面代码是用 throw new Error("a"); 抛出的,则 Generator 函数内部不能捕获到这个异常。

如果 Generator 函数体内部没有部署 try...catch 代码块,则遍历器对象 throw 方法抛出的异常将会被外部 try...catch 捕获。

如果 Generator 内部和外部都没有部署 try...catch 方法,遍历器对象 throw 方法会让程序直接报错中断进行。

throw 方法被 Generator 函数内部捕获后,会附带执行下一条 yield 表达式。也就说说会附带执行一次 next 方法。

var gen = function* gen() {
    try {
        yield console.log('a');
    } catch (e) {
        // ...
    }
    yield console.log('b');
    yield console.log('c');
}

var g = gen();
g.next() // a
g.throw() // b
g.next() // c

如果遍历器指针指向的位置不在 try...catch 内部了,在发生异常不会被 Generator 函数内部捕获。

var gen = function* gen() {
    try {
        yield console.log('a');
    } catch (e) {
        // ...
    }
    yield console.log('b');
    yield console.log('c');
}

var g = gen();
g.next()
g.next()
g.throw()    // 直接报错中止

throw 命令和 throw 方法是无关的,两者互不影响。

var gen = function* gen(){
  yield console.log('hello');
  yield console.log('world');
}
var g = gen();
g.next();
try {
  throw new Error();
} catch (e) {
  g.next();
}
// hello
// world

 一旦 Generator 执行过程中抛出异常,且没有被内部捕获,就不会在执行下去了。如果在调用 next 方法,则会返回 {done: true, value: undefined},即 JavaScript 引擎认为此 Generator 函数已经运行结束。

function* g() {
    yield 1;
    console.log('throwing an exception');
    throw new Error('generator broke!');
    yield 2;
    yield 3;
}

function log(generator) {
    var v;
    try {
        v = generator.next();
        console.log('第一次运行next方法', v);
    } catch (err) {
        console.log('捕捉错误', v);
    }
    // 第一次运行next方法 {value: 1, done: false}
    
    try {
        v = generator.next();
        console.log('第二次运行next方法', v);
    } catch (err) {
        console.log('捕捉错误', v);
    }
    // throwing an exception
    // 捕捉错误 {value: 1, done: false}

    try {
        v = generator.next();
        console.log('第三次运行next方法', v);
    } catch (err) {
        console.log('捕捉错误', v);
    }
    // 第三次运行next方法 {value: undefined, done: true}
}

log(g());

Generator.prototype.return()

返回给定的值,并终止遍历 Generator 函数。

function* gen() {
  yield 1;
  yield 2;
  yield 3;
}
var g = gen();
g.next()        // { value: 1, done: false }
g.return('foo') // { value: "foo", done: true }
g.next()        // { value: undefined, done: true }

如果调用 return 方法时不传参数,则返回值的 value 为 undefined。

function* gen() {
  yield 1;
  yield 2;
  yield 3;
}

var g = gen();

g.next()        // { value: 1, done: false }
g.return()      // { value: undefined, done: true }

如果 Generator 函数内部有 try...finally 代码块,且正在执行 try 代码块,那么 return() 方法会导致立刻进入 finally 代码块,执行完以后,整个函数才会结束。

function* numbers () {
  yield 1;
  try {
    yield 2;
    yield 3;
  } finally {
    yield 4;
    yield 5;
  }
  yield 6;
}
var g = numbers();
g.next() // { value: 1, done: false }
g.next() // { value: 2, done: false }
g.return(7) // { value: 4, done: false }
g.next() // { value: 5, done: false }
g.next() // { value: 7, done: true }

上面代码,调用 return() 方法后,就开始执行 finally 代码块,不执行 try 剩下的代码了,等到 finally 代码块执行完,再返回 return() 方法指定的返回值。

next()、throw()、return() 的共同点

让 Generator 函数恢复执行,并使用不同的语句替换 yield 表达式。

next() 是将表达式替换成一个值。

throw() 是将表达式替换成一个 throw 语句。

return() 是将表达式替换成一个 return 语句。

下一篇关于 Generator

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值