面试官:你是怎么理解ES6中 Generator的?使用场景?
面试官:你是怎么理解ES6中 Generator的?使用场景?
一、介绍
Generator 函数是 ES6 提供的一种异步编程解决方案,语法行为与传统函数完全不同
回顾下上文提到的解决异步的手段:
- 回调函数
- promise
那么,上文我们提到promsie
已经是一种比较流行的解决异步方案,那么为什么还出现Generator
?甚至async/await
呢?
该问题我们留在后面再进行分析,下面先认识下Generator
Generator函数
执行 Generator
函数会返回一个遍历器对象,可以依次遍历 Generator
函数内部的每一个状态
形式上,Generator
函数是一个普通函数,但是有两个特征:
function
关键字与函数名之间有一个星号- 函数体内部使用
yield
表达式,定义不同的内部状态
1 | function* helloWorldGenerator() { |
二、使用
Generator
函数会返回一个遍历器对象,即具有Symbol.iterator
属性,并且返回给自己
1 | function* gen(){ |
通过yield
关键字可以暂停generator
函数返回的遍历器对象的状态
1 | function* helloWorldGenerator() { |
上述存在三个状态:hello
、world
、return
通过next
方法才会遍历到下一个内部状态,其运行逻辑如下:
- 遇到
yield
表达式,就暂停执行后面的操作,并将紧跟在yield
后面的那个表达式的值,作为返回的对象的value
属性值。 - 下一次调用
next
方法时,再继续往下执行,直到遇到下一个yield
表达式 - 如果没有再遇到新的
yield
表达式,就一直运行到函数结束,直到return
语句为止,并将return
语句后面的表达式的值,作为返回的对象的value
属性值。 - 如果该函数没有
return
语句,则返回的对象的value
属性值为undefined
1 | hw.next() |
done
用来判断是否存在下个状态,value
对应状态值
yield
表达式本身没有返回值,或者说总是返回undefined
通过调用next
方法可以带一个参数,该参数就会被当作上一个yield
表达式的返回值
1 | function* foo(x) { |
正因为Generator
函数返回Iterator
对象,因此我们还可以通过for...of
进行遍历
1 | function* foo() { |
原生对象没有遍历接口,通过Generator
函数为它加上这个接口,就能使用for...of
进行遍历了
1 | function* objectEntries(obj) { |
三、异步解决方案
回顾之前展开异步解决的方案:
- 回调函数
- Promise 对象
- generator 函数
- async/await
这里通过文件读取案例,将几种解决异步的方案进行一个比较:
回调函数
所谓回调函数,就是把任务的第二段单独写在一个函数里面,等到重新执行这个任务的时候,再调用这个函数
1 | fs.readFile('/etc/fstab', function (err, data) { |
readFile
函数的第三个参数,就是回调函数,等到操作系统返回了/etc/passwd
这个文件以后,回调函数才会执行
Promise
Promise
就是为了解决回调地狱而产生的,将回调函数的嵌套,改成链式调用
1 | const fs = require('fs'); |
这种链式操作形式,使异步任务的两段执行更清楚了,但是也存在了很明显的问题,代码变得冗杂了,语义化并不强
generator
yield
表达式可以暂停函数执行,next
方法用于恢复函数执行,这使得Generator
函数非常适合将异步任务同步化
1 | const gen = function* () { |
async/await
将上面Generator
函数改成async/await
形式,更为简洁,语义化更强了
1 | const asyncReadFile = async function () { |
区别:
通过上述代码进行分析,将promise
、Generator
、async/await
进行比较:
promise
和async/await
是专门用于处理异步操作的Generator
并不是为异步而设计出来的,它还有其他功能(对象迭代、控制输出、部署Interator
接口…)promise
编写代码相比Generator
、async
更为复杂化,且可读性也稍差Generator
、async
需要与promise
对象搭配处理异步情况async
实质是Generator
的语法糖,相当于会自动执行Generator
函数async
使用上更为简洁,将异步代码以同步的形式进行编写,是处理异步编程的最终方案
四、使用场景
Generator
是异步解决的一种方案,最大特点则是将异步操作同步化表达出来
1 | function* loadUI() { |
包括redux-saga
中间件也充分利用了Generator
特性
1 | import { call, put, takeEvery, takeLatest } from 'redux-saga/effects' |
还能利用Generator
函数,在对象上实现Iterator
接口
1 | function* iterEntries(obj) { |