ES6的新语法。多种角度理解:
- 从语法上,是一个状态机,封装了多个内部状态
- 从执行结果,返回一个遍历器对象,也是一个遍历器生成函数。这个返回的结果可以依次遍历内部每个状态。
- 从执行成面,是对“协程”的实现,可以多个线程(函数)交换控制权
1 语法
1.1 形式上是普通函数,但是带有两个特征(* 和 yield)
generator函数可以不用yield语句,但是yield语句只能用在generator函数中。
函数有三个状态: world 、 girl 、return语句function* hello(){ // 函数命名带有*
yield "world" // 函数体内使用yield语句 yield 翻译 产出
yield "girl"
return "nice"
}
1.2 执行结果是一个指向内部状态的指针对象,既遍历器对象Iterator Object
let ge = hello()
1.3 调用遍历器对象的next方法,使指针移到下一个状态
既从函数头部或上次停下的地方开始执行,直到遇到下一个yield语句或retrun语句为止。
next方法返回一个对象,value属性为当前yield或return后面表达式的值,done属性为遍历器是否结束ge.nxet()
// {value: "world", done: false}
ge.nxet()
// {value: "girl", done: false}
ge.nxet()
// {value: "nice", done: true}
ge.nxet()
// {value: undefined, done: true} //如果运行完毕,则value属性为undefined
1.3.1 yield 语句和其他语句一起使用,
理解next执行顺序以及yield暂停位置
同时与其他表达式一起,需要用括号包住
1.3.2 只有yield的执行结果
执行完yield 2后,并不知道已经执行完,调用还会继续执行,才返回done:true
1.4 next参数
yield
表达式本身没有返回值,或者说总是返回undefined,
next方法可以带一个参数,作为上一条yield语句的返回值
1.5 generator生成的遍历器对象可以被for…of自动遍历,无需调用next
function* foo() {
yield 1
yield 2
yield 3
yield 4
return 5
}
for(let v of foo()){
console.log(v)
}
1.6 Generator.prototype.throw()
参考
可在函数体外抛出错误,然后再generator函数体内捕获
var a = function*() {
try {
yield
}catch(e){
console.log("内部捕获" + e)
}
}
var i = a()
a.throw("a")
// 内部捕获a
2 在异步操作中的应用
generator函数可以暂停执行和恢复执行,这是它能封装异步任务的更本原因。
函数体内外数据交换 + 错误处理机制 使其成为异步编程的完整解决方案。
2.1 手动控制流程
var fetch = require('node-fetch')
//封装异步操作,完全是同步写法
function* gen() {
var url = "http://api.xxxxxx"
var result = yield fetch(url)
consoel.log(result)
}
// 执行
var g = gen()
// 启动异步操作
var result = g.next()
result.value
.then((data){
return data.json()
}).then(function(data){
// 异步操作成成功后继续向下执行,并注入异步操作结果
g.next(data)
})
优点: generator函数将异步操作写成同步写法,很简洁
缺点:流程管理不方便,需要手动判断何时执行第一阶段,以及后续阶段
2.2 Thunk自动控制流程
这个时候就会用到Thunk函数(只有一个参数,且这个参数为callback形式)自动执行generator函数。
//自动执行以及流程控制函数
function run(fn){
let gen = fn()
//let result = gen.next()
//if(result.done) return
//result.value(function(err,data) {
//let result = gen.next(data)
//if(result.done) return
//result.value()
//})
function next(err, date){
let result = gen.next(data)
if(result.done) return
result.value(next)
}
next()
}
// generator函数封装的异步任务 唯一的要求是:yield后面每一个异步操作都是一个Thunk函数
function* fn(){
let f1 = yield readFile("fileA")
let f1 = yield readFile("fileA")
let f1 = yield readFile("fileA")
}
// 执行
run(fn)
2.3 Promise自动控制
Thunk函数不是generator函数自动执行的唯一方案,关键有一种机制接受和交换程序的执行权
除了Thunk这种用回调函数,Promise对象也可以做到
唯一要求也是:yield后面是promise对象。
2.4 co模块是常用的自动执行及控制流程的通用函数工具。支持thunk和promise两种模式。
3 实际应用
在eggjs中controller中用到