ES 6 系列之三种异步 - Generator 函数
介绍
Generator
是 ES 6
提供的一种异步编程解决方案。
- 函数定义时,在
funciton
后面添加一个星号(*
) - 函数体内部使用
yield
表达式
1 | // 普通函数 |
Generator
函数的调用方式与普通函数是一样的,在函数名后面加上一对圆括号。
yield 表达式
yield
表达式就是 Generator
的暂停标志。
- 遇到
yield
表达式,就会停止执行后面的操作,yield
后面表达式的值,作为返回对象的value
属性值 - 下次调用
next
方法时,才会继续执行代码,直到下一个yield
- 如果没有遇到遇到
yield
,则运行至结束,看是否有return
,如果有,则return
后面的值作为返回对象的value
- 如果没有
yield
或return
,则返回value
为undefined
1 | function* generator() { |
在上面的代码中,我们可以来感受上面的用途。
只有在调用 next
方法将指针移到这一句时,才会进行求值。
与 return
不同的地方在于,return
不具备记忆功能,也就说,在一个函数中,return
只能执行一次,而 yield 具备记忆功能,可以通过 next
方法继续执行代码。
next 方法
next 普通使用
next
方法就是 generator
函数的内置执行器,如果遇到 yield
时,只有通过调用 next
,才会继续执行代码。
而 next
代码的执行值,我们可以打印来看,会是一个对象,带有 value
和 done
两个属性。
1 | function* generator() { |
value
值显示的是yield
或者return
后面的返回值。done
是一个布尔值,表示遍历是否结束,true
表示结束。- 当然,
return
只能有一个,且return
之后不能有yield
,因为return
,就会让done
变成true
。
next 方法参数
next
方法可以带一个参数,该参数会被当作上一个 yield
表达式的值。
1 | function* generator() { |
当然,这个是会被当作上一个 yield
表达式的值,是在函数内部使用,并不是在 next
返回对象中作为 value
的键值返回。他会替换 generator
函数体内的上一个 yield
值并赋值给我们定义的变量 value
。
for…of 循环
for...of
循环可以自动遍历 Generator
函数运行时生成的 Interator
对象,不需要手动调用 next
方法。
1 | function* gen() { |
注意:如果 done
为 true
,则会被立刻终止,所有最后一句 return
语句不在循环中。
处理 for...of
循环之外,扩展运算符,解构赋值和 Array.from
方法内部调用的,都是遍历器接口。
都可以将 Generator
函数作为返回的 Interator
对象,作为参数。
1 | function* gen() { |
yield*
yield*
就是 用来在一个 Generator
函数里面执行另一个 Generator
函数。
1 | function* generator1() { |
我们在 generator
函数中,使用了 yield*
,后面调用 generator1
函数。
然后我们就可以直接使用 next
函数,跟普通的 generator
函数一样使用就可以。
Generator.prototype.throw() / return()
throw()
Generator
函数返回的遍历器对象,都会有个 throw
方法,可以在函数体外抛出错误,然后在 Generator
函数体内捕获。
1 | function* generator() { |
在上面的例子中,我们可以看出,第一个 throw
被函数体内部的 catch
语句捕获,然而第二个错误,在内部已经执行过 catch
,所有这个错误就被抛出来了,被外面的 try...catch
捕获。
return()
Generator
函数返回的对象中,还有一个 return
方法,可以返回给定的值,并且结束 Generator
函数。
1 | function* generator() { |
从上面的例子,我们可以看出,执行 return
之后,后面的 done
就会变为 true
,且传入的参数会作为返回对象的 value
值。
在以后调用时,都会被调整为 undefined
,其实就相当于,将 generator
函数中的添加一个 return
。
Generator 函数的异步操作
我们使用一个 readFile
,使用 setTimeout
假装调用接口,然后调用 gen.next()
接口返回对象的 value
属性值进行下一步操作。
1 | const readFile = function (fileName, ms) { |
yield
后面用来调用异步函数,然后返回结果输出。