Generator函数

本文深入探讨ES6中的Generator函数,包括其基本用法、yield关键字的功能、next方法的作用及返回值解析,同时涵盖return和throw方法的应用场景。

Generator

Generator函数是ES6提供的异步编程的解决方案。它和普通的函数有第一定的区别。 在普通函数后面加上一对括号那么普通函数会执行并返回结果。 而Generator函数并不执行, 它返回的也不是运行结果, 而是一个遍历器对象.

yield和next

yield用来暂停后面的操作, 我们可以使用next方法进行取值.
next方法返回的是一个对象,这个对象有连个属性: valuedone, value表示此次的值, done表示是否结束函数.

//next返回一个对象
//value值: 上一个yield后面的值作为value值(注意不是yield的返回值)
//done表示是否结束, true表示结束
function* gene() {
    yield 1
    yield 2
    return 3
}
var gg = gene() //返回遍历器对象
gg.next()   //{value: 1, done: false}
gg.next()   //{value: 2, done: false}
gg.next()   //{value: 3, done: true}

通过next移动指针, 移向下一个状态. 即yield.

  • yield后面的值是next对象中的value
  • yield的返回值是undefined
function* gen() {
    var a = yield 1
    console.log('a')
    console.log(a)
    var b = yield a+1
    console.log('b')
    console.log(b)
    return 3
}

var g = gen()
g.next()    
//{value: 1, done: false}

g.next()    //不给next传值,上一次yield就返回undefined,也即a为undeinfed. value为undefined+1得到NaN
/*
a
undefined
{value: NaN, done: false}
*/

g.next()    //不给next传值,上一次yield就返回undefined,也即c为undeinfed
/*
b
undefined
{value: 3, done: true}
*/
  • 若给next传参, 则next参数是上一次的yield的值
//next的参数是上一次yield的值
var ge = gen()
ge.next()
//{value: 1, done: false}

/*
上一次yield返回值为10并赋值给a,a也为10。value为这次yield后面的值即a+1
*/
ge.next(10) 
/*
a
10
{value: 11, done: false}
*/

/*
next参数为空, 则上一次yield返回值为undefined并赋值给b, 则b为undefined
*/
ge.next()
/*
b
undefined
{value: 3, done: false}
*/

return

return函数是Generator.prototype上的函数. 它的作用是结束Generator函数并返回. 可以给人return传送一个参数1表示返回值. 此时返回的对象是

{value: 1, done: true}

donetrue表示函数结束, 即使函数没有执行完毕.

//代码来自阮一峰的教程
function* gen() { 
  yield 1;
  yield 2;
  yield 3;
}

var g = gen();

//正常结束
g.next();        // { value: 1, done: false }
g.next();        // { value: 2, done: false }
g.next();        // { value: 3, done: true }

//return结束
g.next();        // { value: 1, done: false }
g.return("foo"); // { value: "foo", done: true }
g.next();        // { value: undefined, done: true }

上例中, 执行g.return('foo')结束Generator函数的执行, 可以看到value值为foo, donetrue. 对比正常结束, 可以看清区别.

throw

Generator.prototype.throw用来向生成器抛出异常,并恢复生成器的执行,返回带有 donevalue 两个属性的对象。
通过throw生成的异常既可以在Generator函数体内捕获, 也可以在函数体外捕获. 捕获顺序是先函数体内捕获, 当函数体内捕获完时, 再函数体外捕获.

//代码来自阮一峰的教程
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

第一个throw被内部捕获, 此时内部抛出异常. 第二次抛出的异常是由外部的catch抛出的. 因为内部的catch已经执行过一次了, 而且内部没有更多的catch用于捕获第二次异常, 则第二次异常会被外部捕获.

### 什么是 Generator 函数 Generator 函数是 ES6 中引入的一种特殊类型的函数,它可以返回一个迭代器对象。通过该迭代器对象,可以逐步执行 Generator 函数中的代码并获取其内部的值[^4]。 #### 基本语法 定义一个 Generator 函数时,在 `function` 关键字后面加上星号 (`*`) 表示这是一个 Generator 函数。在函数体中使用 `yield` 关键字暂停和恢复函数的执行,并返回相应的值[^3]。 ```javascript function* myGenerator() { yield 1; yield 2; yield 3; } const generator = myGenerator(); console.log(generator.next().value); // 输出 1 console.log(generator.next().value); // 输出 2 console.log(generator.next().value); // 输出 3 ``` 上述代码展示了如何创建一个简单的 Generator 函数以及如何通过 `.next()` 方法逐步获取生成器返回的值。 --- ### Generator 的工作原理 当调用一个 Generator 函数时,实际上并不会立即执行其中的代码,而是返回一个迭代器对象。每次调用迭代器的 `.next()` 方法时,都会从上一次停止的地方继续执行直到遇到下一个 `yield` 或者到达函数结束位置。 如果 Generator 函数中有多个 `yield` 表达式,则可以通过多次调用 `.next()` 来逐一处理这些表达式的值。 --- ### 复杂场景下的 Generator 使用 #### 调用嵌套的 Generator 函数 默认情况下,直接在一个 Generator 函数中调用另一个 Generator 函数不会自动展开后者的结果。为了实现这一点,需要显式地使用 `yield*` 将子生成器的内容合并到父生成器中[^5]。 ```javascript function* foo() { yield 'aaa'; yield 'bbb'; } function* bar() { yield* foo(); // 展开foo生成器的内容 yield 'ccc'; yield 'ddd'; } let iterator = bar(); for (let value of iterator) { console.log(value); } // aaa bbb ccc ddd ``` 在这里可以看到,`yield*` 提供了一种简洁的方式来组合不同层次的生成逻辑。 --- ### 实现异步操作的同步化表达 利用 Generator 函数配合协程模式(coroutine),能够简化复杂的异步流程管理问题。通常结合第三方库如 co.js 或原生的 async/await 功能来完成这一目标[^1]。 下面是一个基于手动驱动的方式模拟异步请求的例子: ```javascript function wait(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } function* taskScheduler() { console.log('Task started'); yield wait(1000); // 模拟耗时任务 console.log('After first delay'); yield wait(2000); // 又一段延迟 console.log('All tasks completed'); } const schedulerIterator = taskScheduler(); (function exec() { const result = schedulerIterator.next(); if (!result.done) { result.value.then(() => exec()); } })(); ``` 此例子说明了即使面对多阶段延时计算的情况,借助于 Generators 和 Promises 结合也可以让程序看起来像顺序执行那样直观易懂[^1]。 --- ### 总结 - **基本功能**:提供一种机制允许开发者分段运行 JavaScript 程序片段。 - **核心概念**:依靠 `yield/yield*` 控制权转移;依赖 `.next()` 推动进程向前发展。 - **实际用途**:除了作为普通的迭代工具外,还特别适合用来解决复杂状态转换或者协调长时间等待的任务链路等问题。 --- 问题
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值