虽然现在使用 async 函数 就可以替代 Generator 执行器了,不过了解下 Generator 执行器的原理还是挺有必要的。如果你不了解 Generator,那么你需要看这里。例子都可以在 Console 中运行的(谷歌版本 76.0.3809.100),都是以最新浏览器支持的 JavaScript 特性来编写的,不考虑兼容性。
Generator 是 Generator function 运行后返回的对象。
有 Generator next 函数的特性,next 函数运行后会返回如下结构:
{ value: 'world', done: false }
或者
{ value: undefined, done: true }
那么我们可以使用递归运行 next 函数,如果 done 为 true 则停止 next 函数的运行。
yield 表达式本身没有返回值,或者说总是返回 undefined。
next 方法可以带一个参数,该参数就会被当作上一个 yield 表达式的返回值。
由于 next 方法的参数表示上一个 yield 表达式的返回值,所以在第一次使用 next 方法时,传递参数是无效的。
function* test() {
const a = yield "a"
console.log(a)
return true
}
const gen = test()
// 第一次 next() 会卡在 yield a 中,next 返回 {value: "a", done: false}
// 第一个 next() 函数的参数是无效的,无需传递
const nextOne = gen.next()
// 传递参数 nextOne.value
// test 函数中的 console.log 输出 'a'
// 第二次的 next() 返回值为 {value: true, done: true}
gen.next(nextOne.value)
简单的执行器代码如下:
/**
* generator 执行器
* @param {Function} func generator 函数
* @return {Promise} 返回一个 Promise 对象
*/
function generatorExecuter(func) {
const gen = func()
function recursion(prevValue) {
const next = gen.next(prevValue)
const value = next.value
const done = next.done
if (done) {
return Promise.resolve(value)
} else {
return recursion(value)
}
}
return recursion()
}
代码并不复杂,最简单的执行器就出来了。如果你是一步一步的看文章过来的,都理解了原理,那么这些代码也很好理解。
上面的代码执行如下的 Generator 函数是不正确的:
function* test() {
const a = yield Promise.resolve('a')
console.log(a)
return true
}
generatorExecuter(test)
运行上面的代码后,test 函数 console.log 输出的不是 a,而是 Promise {<resolved>: "a"}。
那么我们代码需要这样处理:
/**
* generator 执行器
* @param {Function} func generator 函数
* @return {Promise} 返回一个 Promise 对象
*/
function generatorExecuter(func) {
const gen = func()
function recursion(prevValue) {
const next = gen.next(prevValue)
const value = next.value
const done = next.done
if (done) {
return Promise.resolve(value)
} else {
return Promise.resolve(value).then(finalValue => {
return recursion(finalValue)
})
}
}
return recursion()
}
这样就再运行 generatorExecuter(test) 就没问题了。
上面的代码执行如下的 Generator 函数是不正确的:
function* aFun() {
return 'a'
}
function* test() {
const a = yield aFun
console.log(a)
return true
}
generatorExecuter(test)
运行上面的代码后,test 函数 console.log 输出的不是 a,而是输出下面的字符串:
ƒ* aFun() {
return 'a'
}
那么我们代码需要这样处理:
/**
* 是否是 generator 函数
*/
function isGerneratorFunction(target) {
if (
Object.prototype.toString.apply(target) === '[object GeneratorFunction]'
) {
return true
} else {
return false
}
}
/**
* 运行 generator
* @param {GeneratorFunction} generator generator函数运行
*/
function generatorExecuter(generatorFunc) {
const generator = generatorFunc()
function recursionGeneratorNext(prevValue) {
const next = generator.next(prevValue)
let value = next.value
const done = next.done
if (isGerneratorFunction(value)) {
value = generatorExecuter(value)
}
if (done) {
return Promise.resolve(value)
} else {
// value 可能是 Promise 对象
return Promise.resolve(value).then(finalValue => {
return recursionGeneratorNext(finalValue)
})
}
}
return recursionGeneratorNext()
}
这样就再运行 generatorExecuter(test) 就没问题了。
上面的代码执行如下的 Generator 函数是不正确的:
function* aFun(value) {
return value
}
function* test() {
const a = yield aFun('a')
console.log(a)
return true
}
generatorExecuter(test)
运行上面的代码后,test 函数 console.log 输出的不是 a,而是 aFun {<suspended>}。
那么我们代码需要这样处理:
/**
* 是否是 generator
*/
function isGernerator(target) {
if (Object.prototype.toString.apply(target) === '[object Generator]') {
return true
} else {
return false
}
}
/**
* 是否是 generator 函数
*/
function isGerneratorFunction(target) {
if (
Object.prototype.toString.apply(target) === '[object GeneratorFunction]'
) {
return true
} else {
return false
}
}
/**
* 运行 generator
* @param {Generator} generator generator函数运行后返回的对象
*/
function runGernerator(generator) {
function recursionGeneratorNext(prevValue) {
const next = generator.next(prevValue)
let value = next.value
const done = next.done
if (isGernerator(value)) {
value = runGernerator(value)
} else if (isGerneratorFunction(value)) {
value = generatorExecuter(value)
}
if (done) {
return Promise.resolve(value)
} else {
// value 可能是 Promise 对象
return Promise.resolve(value).then(finalValue => {
return recursionGeneratorNext(finalValue)
})
}
}
return recursionGeneratorNext()
}
/**
* generator 执行器
* @param {Generator || GeneratorFunction} generatorFunc generator 函数或者 generator
* @return {Promise} 返回一个 Promise 对象
*/
function generatorExecuter(generatorFunc) {
let generator
if (isGerneratorFunction(generatorFunc)) {
generator = generatorFunc()
} else if (isGernerator(generatorFunc)) {
generator = generatorFunc
} else {
throw new TypeError(
'Expected the generatorFunc to be a GeneratorFuntion or Generator.'
)
}
return runGernerator(generator)
}
这样就再运行 generatorExecuter(test) 就没问题了。
Generator 执行器没想的那么难,花点时间就可以吃透了。
/**
* 是否是 generator
*/
function isGernerator(target) {
if (Object.prototype.toString.apply(target) === '[object Generator]') {
return true
} else {
return false
}
}
/**
* 是否是 generator 函数
*/
function isGerneratorFunction(target) {
if (
Object.prototype.toString.apply(target) === '[object GeneratorFunction]'
) {
return true
} else {
return false
}
}
/**
* 运行 generator
* @param {Generator} generator generator函数运行后返回的对象
*/
function runGernerator(generator) {
function recursionGeneratorNext(prevValue) {
const next = generator.next(prevValue)
let value = next.value
const done = next.done
if (isGernerator(value)) {
value = runGernerator(value)
} else if (isGerneratorFunction(value)) {
value = generatorExecuter(value)
}
if (done) {
return Promise.resolve(value)
} else {
// value 可能是 Promise 对象
return Promise.resolve(value).then(finalValue => {
return recursionGeneratorNext(finalValue)
})
}
}
return recursionGeneratorNext()
}
/**
* generator 执行器
* @param {Generator || GeneratorFunction} generatorFunc generator 函数或者 generator
* @return {Promise} 返回一个 Promise 对象
*/
function generatorExecuter(generatorFunc) {
let generator
if (isGerneratorFunction(generatorFunc)) {
generator = generatorFunc()
} else if (isGernerator(generatorFunc)) {
generator = generatorFunc
} else {
throw new TypeError(
'Expected the generatorFunc to be a GeneratorFuntion or Generator.'
)
}
return runGernerator(generator)
}
运行例子如下,直接在谷歌 console 运行即可:
// 例子
function* one() {
return 'one'
}
function* two() {
return yield 'two'
}
function* three() {
return Promise.resolve('three')
}
function* four() {
return yield Promise.resolve('four')
}
function* five() {
const a = yield new Promise(resolve => {
setTimeout(() => {
resolve('waiting five over')
}, 1000)
})
console.log(a)
return 'five'
}
function* all() {
const a = yield one()
console.log(a)
const b = yield two()
console.log(b)
const c = yield three()
console.log(c)
const d = yield four()
console.log(d)
const e = yield five()
console.log(e)
return 'over'
}
generatorExecuter(all).then(value => {
console.log(value)
})
// 或者
// generatorExecuter(all()).then(value => {
// console.log(value)
// })
Generator与async function都是返回一个特定类型的对象:Generator: 一个类似{ value: XXX, done: true }这样结构的Object,Async: 始终返回一个Promise,使用await或者.then()来获取返回值
由于Generator可以暂停函数执行,返回任意表达式的值,这使得 Generator有多种应用场景,这篇文章简单整理一些Generator的使用场景:异步操作的同步化表达、控制流管理、部署 Iterator 接口、作为数据结构
Generator 函数有多种理解角度。语法上,首先可以把它理解成,Generator 函数是一个状态机,封装了多个内部状态。执行 Generator 函数会返回一个遍历器对象,也就是说,Generator 函数除了状态机,还是一个遍历器对象生成函数。返回的遍历器对象,可以依次遍历 Generator 函数内部的每一个状态。
Generator函数是ES6标准中提出的一种异步编程的解决方案。这种函数与普通函数最大的区别在于它可以暂停执行,又可以从暂停的位置恢复继续执行。从语法上看,Generator函数就是一个状态机,封装了许多内部状态。
es6 generator函数,我们都知道asycn和await是generator函数的语法糖,那么genertaor怎么样才能实现asycn和await的功能呢?thunk函数 将函数替换成一个只接受回调函数作为参数的单参数函数
状态机,封装了多个内部状态;返回一个遍历器对象,通过改对象可以一次遍历Generator函数内部的每一个状态;带*号,yeild表达式定义不同的内部状态;调用 Generator 函数后,该函数并不执行,返回的也不是函数运行结果
最近,为了更好地理解Redux Sagas的工作原理,我重学了JavaScript generators的知识,我把从网上收集到的各种知识点浓缩到一篇文章里,我希望这篇文章既通俗易懂,又足够严谨,可以作为初学者的generators使用指南。
实际上Generator就是遍历器的一个生成器,我们可以用Generator来生成一个遍历器。Generator有两个明显的特点:第一个是function关键字与函数名之间有一个星号,一般而言是将两者写在一起的。第二个是在函数体内部有一个yield的关键字
这是一只会说话的猫的一些代码,可能是当今互联网上最重要的一种应用。它看起来有点像一个函数,对吗?这被称为生成器-函数,它与函数有很多共同之处。但你马上就能看到两个不同之处。
可以把生成器看作一个值的生产者,我们通过迭代器接口的next 调用一次提取出一个值,所以严格来讲,生成器本身并不是 iterrable,尽管执行一个生成器就得到了一个迭代器
内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!