在之前的文章引见了传统异步的完成计划,本文将引见ES6中的一种全新的异步计划–Generator函数。
generator简介
简朴引见一下generator的道理和语法,(更细致内容请看ECMAScript 6 入门,本文只引见和异步相干的中心内容)
基础语法
经由过程一个简朴的例子来相识generator
函数
function* MyGenerator() {
yield 'yield的寄义是:实行此处时,停息实行当前函数'
yield '停息以后的函数可以用next要领继承实行'
return '碰到return以后会真正完毕,done会变成true'
}
const gen = MyGenerator() //实行后返回的是一个指针,可以应用该指针实行next要领
console.log(gen.next()) // {value: "yield的寄义是:实行此处时,停息实行当前函数“, done: false}
console.log(gen.next())// {value: "停息以后的函数可以用next要领继承实行", done: false}
console.log(gen.next())// {value: "碰到return以后会真正完毕,done会变成true", done: true}
数据交互
数据交互指的是可以经由过程yield把当前实行的效果传出,也可以运用next把外部参数传入
function* MyGenerator(){
const result = yield '传出的数据'
return result
}
const gen = MyGenerator()
console.log(gen.next())// generatorAndAsync2.html:19 {value: "传出的数据", done: false}
console.log(gen.next('传入的数据'))// {value: "传入的数据", done: true}
交互的参数范例固然也可以是函数:
function sayHi(){
console.log('hi');
}
function* MyGenerator(){
const result = yield sayHi()
return
}
const gen = MyGenerator()
const result = gen.next()// {value: sayHi, done: false}
result.value() // 一般输出'hi'
具有以上最中心的两个特征以后,generator就可以举行异步操纵封装。
异步使命封装
起首,连系异步使命的特性以及前文提到的genrator函数的特征,提炼出运用generator封装异步操纵的中心思绪:
- 在异步使命实行时,运用
yield
交出实行权 - 在异步使命完毕后,运用
next
交还实行权
起步
从一个最简朴的例子最先:
// 1. 起首写一个异步使命,在一秒后返回特定字符串
function asyncTask(callback){
setTimeout(()=>{
callback('Hello Leo')
}, 1000)
}
// 2. 接下来写出希冀实行的递次
function* runTask() {
let text = yield asyncTask
console.log(text) // 我们希冀这里一般输出Hello Leo
}
// 3. 依据希冀值实行函数
const gen = runTask()// 此时实行权已交出
gen.next().value(function (text) {// 实行asyncTask并传入callback ,症结点在于在callback里挪用next交还实行权
gen.next(text)
})
起首,这段代码虽然很粗拙,然则已反应了运用generator
封装异步使命的中心头脑。最直观的受益就是:runTask
的内容看起来很像同步代码,条理清楚,很合适浏览。
然则上面第3部份关于实行的代码很不天真,我们不能每次都这么写一段,因而接下来的目的就是完成一个使命实行器。
自动使命实行器
一样的,先思索中心的思绪:要想让某个generator
函数自动实行,不过就是一个while
轮回:
1. 假如当前yield返回值的done属性为true,申明使命已实行完成;
2. 假如当前yield返回值的done属性为false,申明使命还未实行完成,则继承实行next,同时要注意参数通报
依据剖析完成以下的实行器:
function autoExecute(task) {
const gen = task()
let result = gen.next()
while(true){
if(result.done){
break // 实行完毕
return
}
console.log(result.value)//为了便于视察 我们加上console.log
result = gen.next(result.value) // 每次都应该重写result 猎取最新效果
}
}
function* simpleTask(){
yield 1
yield 2
yield 3
return
}
autoExecute(simpleTask)// 测试我们写的自动实行器 可以准确输出123
上面的实行器已有了雏形,然则关于前面例子中,result.value为函数的状况还没有处置惩罚,因而还需要轻微补充:
function isFunction(source){
return Object.prototype.toString.call(source) === "[object Function]"
}
function autoExecute(task) {
const gen = task()
let result = gen.next()
let isRuningAsync = false // 因为加入了异步处置惩罚,所以要增添一个标志位防止反复进入轮回体
while (!isRuningAsync) {
if (result.done) {
return
}
console.log(result.value)
/* start 补充的处置惩罚函数 */
if (isFunction(result.value)) {
isRuningAsync = true
const callback = (arg) => {
result = gen.next(arg) // 中心代码
isRuningAsync = false
}
result.value(callback)
/* end 补充的处置惩罚函数 */
} else {
result = gen.next(result.value)
}
}
}
autoExecute(runTask) // 试着用这个自动实行器实行之前的异步使命
上面这个自动实行器,就完成了generator对异步使命的封装。
总结
本文扼要引见了generator函数的一些特征,重点在于申明怎样运用generator
函数对异步使命举行封装,从而可以让异步代码编写的越发清楚。
再次强调:用generator
函数对异步使命封装的头脑是很明白的–掌握 Generator 函数的流程,在恰当的机遇吸收和交还顺序的实行权,然则详细的完成体式格局并不唯一,比方本文用的是最简朴直接的回调函数体式格局,在阮一峰先生的《es6入门》教程里,也有运用thunk思绪来解说的部份,可以自行查阅。