在 ES6 中,我们已经熟悉了箭头函数、let 和 const 关键字,以及解构赋值等功能。但今天我们要来探讨一种变化比较大的新增语法——ES7 中的 Generator 函数。
什么是 Generator 函数?
Generator 函数是 ES6 中新增的语法,是一种可以暂停和恢复执行的函数。也就是说,它可以执行一个段落,然后暂停执行,等待下一次调用继续执行,这种方式与普通函数完全不同。
定义一个 Generator 函数,需要在函数名前加上一个星号( * )。
function* myGenerator() { yield 'one'; yield 'two'; return 'three'; } let g = myGenerator(); console.log(g.next()); // { value: 'one', done: false } console.log(g.next()); // { value: 'two', done: false } console.log(g.next()); // { value: 'three', done: true } console.log(g.next()); // { value: undefined, done: true }
在上面的代码中,我们定义了一个 myGenerator 函数,这个函数中用到了 yield 关键字。yield 可以将函数执行过程中的值暂停并输出,并且可以保存函数的状态。
当函数被调用后,我们可以通过调用 next 方法获取函数的值。第一次调用 next 方法时,myGenerator 函数开始执行,直到遇到第一个 yield 关键字为止,输出值为 'one',done 属性为 false。第二次调用 next 方法时,函数从上次 yield 的位置继续执行,直到遇到下一个 yield 关键字,输出值为 'two',done 属性仍然为 false。第三次调用 next 方法时,由于已经没有下一个 yield 关键字了,函数执行完毕,输出值为 'three',done 属性为 true。最后一次调用 next 方法时,函数已经执行完毕,输出结果为 undefined。
Generator 函数的特点
我们可以从上面的代码中看出 Generator 函数的几个特点:
- 使用 yield 关键字可以中断函数的执行,并保存函数的执行状态和值。
- 可以使用 next 方法恢复函数的执行。
- 在函数执行完毕后,再次调用 next 方法返回的值为 undefined,done 属性为 true。
- 由于调用 next 方法时只执行一些代码,因此可以有效地控制代码的执行顺序。
示例:生成含有斐波那契数列的 Generator 函数
我们来看一个更具体的示例:一个生成含有斐波那契数列的 Generator 函数。
function* fibonacci() { let a = 1; let b = 1; while (true) { yield a; [a, b] = [b, a + b]; } } let g = fibonacci(); console.log(g.next().value); // 1 console.log(g.next().value); // 1 console.log(g.next().value); // 2 console.log(g.next().value); // 3 console.log(g.next().value); // 5 console.log(g.next().value); // 8 ...
在上面的代码中,我们定义了一个 fibonacci 函数,用来生成一个斐波那契数列。首先我们初始化了两个数 a 和 b,然后无限循环,在每次循环中我们使用 yield 关键字输出当前的斐波那契数列值 a,并将 a 和 b 的值进行替换。
在获取到 Generator 对象之后,我们可以通过调用 next 方法来输出斐波那契数列的下一个值。每次调用 next 方法,都会从上一个 yield 处开始执行,输出的值取决于当前 yield 的值。
Generator 函数的应用
Generator 函数不仅是一种新的语法,同时也可以用于一些常见的应用场景。下面我们介绍一些 Generator 函数的应用。
1. 状态机
一个状态机有固定的状态,每次触发后可以进入到下一个状态中。Generator 函数可以通过 yield 关键字保存当前状态,并在下一次触发时继续执行。
function* stateMachine() { let state = 'off'; while (true) { switch (state) { case 'off': { const click = yield state; if (click) { state = 'on'; } break; } case 'on': { const click = yield state; if (click) { state = 'off'; } break; } } } } let sm = stateMachine(); console.log(sm.next().value); // 'off' console.log(sm.next(true).value); // 'on' console.log(sm.next(true).value); // 'off' ...
在上面的代码中,我们定义了一个 stateMachine 函数,作为一个状态机。state 表示当前的状态,我们从 off 状态开始,等待用户的点击。如果用户点击了,我们将 state 设置为 on 状态,并继续等待下一次的点击事件。
再次点击时,我们返回到 off 状态,并继续等待下一次的点击事件。这样反复循环,就实现了简单的状态机。
2. 异步编程
在异步编程中,我们经常需要通过回调函数来处理异步操作的结果。而在 Generator 函数中,可以通过 yield 关键字暂停函数的执行,并在异步操作完成后继续执行。
function* asyncTask() { const res1 = yield fetchData(); console.log(res1); const res2 = yield fetchData(); console.log(res2); } function fetchData() { return new Promise((resolve) => { setTimeout(() => { resolve('data'); }, 2000); }); } let task = asyncTask(); task.next().value.then((res1) => { task.next(res1).value.then((res2) => { task.next(res2); }); });
在上面的代码中,我们定义了一个 asyncTask 函数用来处理异步操作的结果。在这个函数中,我们使用 yield 关键字暂停函数执行,等待异步操作的结果。
在获取到 Generator 对象之后,我们通过调用 next 方法开始执行 Generator 函数。第一次调用 next 方法得到一个 Promise 对象,需要在 Promise 对象的 resolve 中调用 next 方法并传入第一个异步操作的结果。等到第一个异步操作执行完毕后,会继续执行 asyncTask 函数,并从第一个 yield 处继续运行到第二个 yield 处。
重复以上步骤,即可实现异步操作的同步化处理。
总结
通过上面的示例,我们可以看到 Generator 函数在异步编程和状态机等方面的应用。同时也可以看到 Generator 函数相对于普通函数的一些优势,比如可以暂停函数执行,并保存函数执行状态。但需要注意的是,Generator 函数不能单独执行,需要通过调用 next 方法来获取函数的执行结果。
来源:JavaScript中文网 ,转载请注明来源 本文地址:https://www.javascriptcn.com/post/65925fb1eb4cecbf2d730845