Generator函数

重要概念

1.Generator函数执行后,函数并不会主动往下执行任意一个字符,而是返回一个指向内部状态的指针对象,是一个遍历器对象(Iterator Object)

2.想要函数继续执行,必须要在函数执行时返回的对象上调用next()方法:

注意:

在调用next()后,函数会一直执行到包含yield的表达式,此时,在这个表达式中,会首先执行yield后的表达式(子表达式),并将其作为next函数执行后返回对象的value属性的值,此时函数就暂停执行了,而不再执行父表达式。

上述函数暂停执行的地方,使得Generator函数执行返回的Iterator对象中的next()方法的参数存在有意义。本次调用x.next(arg),这里的arg将会作为上一次yield表达式的返回值,并继续执行上一次yield表达式的父表达式(如果父表达式存在的话)直到下一个yield或者函数结束。

若next()不带参数,则yield返回undefined:

function *gen(x){
    let result = yield x;
    console.log('result: ' + result );
}

let g = gen(5);
g.next();//{value: 5, done: false}
g.next();//result: undefined
         //{value: undefined, done: true}

这里的上一次也包含着:

由于next方法的参数表示上一个yield表达式的返回值,所以在第一次使用next方法时,传递参数是无效的。V8 引擎直接忽略第一次使用next方法时的参数,只有从第二次使用next方法开始,参数才是有效的。

3.函数的返回值会作为最后一次调用next方法返回的对象中value属性的值。{value: 函数返回值, done: true}

 

Generator.prototype.next():

Generator.prototype.return():

Generator.prototype.throw():

next()throw()return()这三个方法本质上是同一件事,可以放在一起理解。它们的作用都是让 Generator 函数恢复执行,并且使用不同的语句替换yield表达式。以上均可查阅参考1

Generator的this

generator函数不能作为构造函数,即不能通过new关键字实例化,但generator函数执行会返回迭代器,这个迭代器规定是generator函数的实例,因此会继承generator的prototype属性。

function *gen(){}

gen.prototype.test = function() {
    console.log('yes');
}

let tmp = gen();

tmp.test();//yes

既然generator函数不能通过new实例化,而且执行也是返回迭代器,那么在generator函数中通过this关键字定义的属性,通过generator返回的迭代器实例是无法访问到的。

function *gen(){
    this.a = 5;
    yield 5;
}

let tmp = gen();
tmp.next();//{ value: 5, done: false }
tmp.next();//{ value: undefined, done: true }
tmp.a;//undefined

让generator函数执行返回的迭代器实例,表现为generator函数通过new关键字创建实例的行为(也就是返回的迭代器对象能够访问generator函数中this定义的属性):

//这里把tmp对象绑定到了foo函数中this关键字上
//因此访问a时,要通过tmp对象去访问
//注意这里的两次调用next方法,只有generator函数执行到指定代码时,相应的操作才会生效

//不过这里是绑定到了tmp对象上,而非绑定到generator函数执行返回的迭代器实例上
function *foo(){
    yield 1+1;
    this.a = 'yes';
}

let tmp = {};
let s = foo.call(tmp);

s.next();//...
s.next();//...

s.a//undefined
tmp.a//yes

改进版:

//将generator函数中this绑定到generator的prototype属性上
//此时属性就定义到了prototype上

function *foo(){
    yield 1 + 1;
    this.a = 'yes';
}

let s = foo.call(foo.prototype);

s.next();
s.next();

s.a//yes

 

tips

Generator函数本身就可以保存状态,因此可以避免常规函数实现状态机时定义状态变量。

参考:

[1].http://es6.ruanyifeng.com

 

### 什么是 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()` 推动进程向前发展。 - **实际用途**:除了作为普通的迭代工具外,还特别适合用来解决复杂状态转换或者协调长时间等待的任务链路等问题。 --- 问题
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值