前言
我们都知道javascript是一门单线程语言,在同一个时间内只能做一件事情。基于这个,对于 异步任务(asynchronous) 需要进行异步编程,一般有一下几种应对策略:
- 回调函数
- 事件模式 ( 回调函数的事件化,在jQuery等类库中非常常见 )
- Promise
- Generator
最近学习了第四种方法也就是Generator函数,就记录一下学习过程(〃’▽’〃)
Generator的概念和用法
上面提到的几种方法都是回调函数来实现的,或者说是对回调函数的封装,Generator 是ES6中提出来的新方法,要说它最大的特点应该就是可以实现函数的暂停、启动。
下面我们来看一个例子:
function ajax(url) {
fetch(url)
.then(data => data.json())
.then(data => dataGen.next(data))
}
// Generator函数是生成器函数 不能直接用来执行 要先生成迭代器
function* steps() {
// 可以停下来
console.log('feching beers')
const beers = yieldajax('http://api.react.beer/v2/search?q=hops&type=beer')
console.log(beers)
console.log('feching wes')
const wes = yield ajax('https://api.github.com/users/CChanggg')
console.log(wes)
console.log('fetching fat joe')
const fatJoe = yield ajax('https://api.discogs.com/artists/51988')
console.log(fatJoe)
}
// 迭代器 Iterator
const dataGen = steps()
dataGen.next()
在这个例子中用到了 FetchAPI 。response.json()返回promise对象,.then方法后,即可返回服务器的responseData。
首先执行 Generator 函数,获取遍历器对象,然后使用 next 方法,执行异步任务的第一阶段。由于 Fetch 模块返回的是一个 Promise 对象,因此要用 then 方法调用下一个next 方法。
在上面的例子中我们可以一个个打印对应API中获取获取到的内容,在所有异步操作需要暂停的地方,都用 yield 语句注明。
我们再来看个详细点的例子:
const inventors = [{
first: 'Albert',
last: 'Einstein',
year: 1879
}, {
first: 'Isaac',
last: 'Newton',
year: 1643
}, {
first: 'Galileo',
last: 'Galilei',
year: 1564
}, {
first: 'Marie',
last: 'Curie',
year: 1867
}, {
first: 'Johannes',
last: 'Kepler',
year: 1571
}]
基于上面的代码,我们现在有一个需求,就是需要让 inventors 里的 Object 一个个输出,这个时候就可以用上我们的 Generator 函数:
function* loop() {
for (const inventor of inventors) {
console.log(inventor)
yield inventors
}
}
const g = loop()
g.next()
g.next()
g.next()
g.next()
g.next()
这时候我们就可以看到对象被一个个输出了

在上面代码中,调用 Generator 函数,也就是function* loop()会返回一个内部指针,也就是 变量器。 执行这个函数不会返回结果,返回的是指针对象。调用指针 g 的 `next`` 方法,会移动内部指针(即执行异步任务的第一段),指向第一个遇到的 yield 语句,也就是输出对象为止。
使用方法进阶
看了上面的例子,我们对Generator 函数也就稍微有了一些理解,但是如果当我们要一个个输出很多内容时候,就需要很多个.next()的调用,就很麻烦,这个时候我们就想要一个方法可以 自动 去输出。
来看看下面这个例子:
function* gen() {
var h = yield Promise.resolve('hello')
console.log(h)
var w = yield Promise.resolve('world')
console.log(w)
}
// const iteratorGen = gen()
// console.log(iteratorGen.next())//用于启动
// console.log(iteratorGen.next('h'))
// console.log(iteratorGen.next('w'))
function co(gen) {
// 实现自动执行generator
// done true
var iter = gen()
var ret = iter.next()
function next(ret) {
ret.value.then(function(res) {
ret = iter.next(res)
if (ret.done) return
next(ret)
})
}
next(ret)
}
co(gen)
在上面的代码中在 function* gen()无论有多少个需要输出的对象都会自动一个个的输出来,这是为什么呢?
上面代码的 co 函数,就是一个 Generator 函数的自动执行器。
在 co 函数中的内部函数 next 先将指针移到 Generator 函数的下一步(iter.next 方法),然后判断 Generator 函数是否结束(ret.done 属性),如果没结束,就将 next
函数再传入 co 函数,否则就直接退出。
总结
当然自动执行器还有多种实现方法,比如promise啥的,这里举出来的只是一个简单的例子。进一步的学习可以看看tj大大在GitHub上的内容。
上面就是学习过程中的总结,有任何不对的地方,欢迎指点。(づ ̄3 ̄)づ╭❤~