JS异步编程(4)-Generator

Generator 是与 Promise 同时由 ES6 引入标准的语法,最早由社区提出和实现
主要用于实现一种新的状态机制管理,让一段代码逻辑可以动态控制分段执行

Generator 新增关键字及函数

* 关键字

* 关键词用于生成 Generator函数* 前后的空格没有影响),但有以下几个限制:

  • 只能用于 function 关键字
  • 不能用于箭头函数
function * fun1 () {} // work
function* fun2 () {} // work
function *fun3 () {} // work
const fun4 = function * () {} // work

const fun5 = *() => {} // 报错!

一旦函数被生成为 Generator函数,这个函数就不能作为构造函数被 new 关键字使用了

function * Fun() {}
const obj = new Fun() {} // 报错!

yield 关键字

yield 的返回值

yield 在功能上和 return 有些类似,都用于将值返回,但有些差异:

  • return 语句之后无法进行任何操作;yield 后续的程序可以通过调用 next 继续执行
  • return 无法和赋值符一起使用;yield 可以和赋值符一起使用
  • return 可以独立使用;yield 必须配合 Generator函数next 函数才能使用
function * test() {
    const first = yield 1
    const second = yield 2
    const third = yield 3
}
const obj = test()
obj.next() // {value: 1, done: false}
obj.next() // {value: 2, done: false}
obj.next() // {value: 3, done: false}
obj.next() // {value: undefined, done: true}

yield 可以拼接的表达式

yield 后面可以拼接多种类型的值

  • 基本类型:yield '123'yield 123yield { name: 'Peter' } 等等
  • 函数:yield function test(){}
  • 表达式:yield 1 + 2 + 3

多个 yield 拼接

yieldyield 拼接时,后一个 yield 作为变量使用,为 undefined

function * test() {
    yield yield
}
const obj = test()
obj.next() // {value: undefined, done: false}

yield 的类函数形式

看一段代码

function * test() {
    yield 1
}
// 上面的写法,等价于下方的写法
function * test() {
    yield(1)
}

yield(1) 咋一看,会让人觉得是 yield 作为一个函数被调用了
其实是 yield 返回了一个使用小括号包裹的表达式(1)
这里纯粹是个人见解,没有找到权威的文档佐证,欢迎指正!

next 函数

第一次 next 调用的传参没有作用

function *test(x) {
    const re = yield 1 + x;
    return re + 2;
}

const obj = test(5);
obj.next(2) // {value: 6, done: false}
obj.next(3) // {value: 5, done: true}

如上

  1. 变量 x 来自生成 Generator对象 时的参数 5
  2. 第一次 next 调用,函数内部开始执行到 yield 返回 1 + 5,因此本次 next 参数无效
  3. 第二次 next 调用,next 的参数 3 赋值给 yield 后面的整个表达式 const re = 3,因此结果为 3 + 2

next 函数参数是对上一个 yield 及后方整个表达式的值覆盖

function *test() {
    console.log(yield 5) // 2
}

const obj = test();
obj.next()
obj.next(2)

第二次 next 调用,yield 5 的值为 2,被参数覆盖

yield 和 next 的返回值

对于 yieldnext 的返回值问题一直比较模糊
{ value: any, done: Boolean } 到底是 yield 的返回值还是 next 的返回值?

下面分别引用了 MDN 上对 yieldnext 返回值的描述

yield关键字实际返回一个IteratorResult对象,它有两个属性,value和done。value属性是对yield表达式求值的结果,而done是false,表示生成器函数尚未完全完成。

参考自yield

next() 方法返回一个包含属性 done 和 value 的对象。该方法也可以通过接受一个参数用以向生成器传值。

参考自Generator.prototype.next()

说实话,从两段描述上来看,yieldnext 返回值都是 { value: any, done: Boolean }

所以写了下方的代码做验证

function *test() {
    console.log(yield 1) // 2
}

const obj = test();
obj.next()
obj.next(2) // { value: undefined, done: true }

从结果上来看

  • yield 返回的是 value
  • next 返回的是 { value: undefined, done: true }

Generator 执行流程

  1. 生成 Generator对象,这时候 Generator函数 内部不会执行
function *test() {
    console.log('start')
    let re = yield 1;
    return re + 2;
}

const testObj = test(); // no console
const testObj2 = test(); // no console

如果生成多个 Generator对象,则各个 Generator对象 保持着各自的执行阶段,互不影响

  1. Generator对象调用 next 函数
    从函数开头或上一个 yield 开始,执行到下一个 yield 或者 return
function *test() {
    console.log('before first yield')
    let first = yield 1;
    console.log('after first yield')
    let second = yield 2;
    console.log('after second yield')
    let third = yield 3;
    console.log('after third yield')
    return re + 2;
}

const testObj = test(); // no console
const firstResult = testObj.next(); // before first yield
const secondResult = testObj.next(); // after first yield
const thirdResult = testObj.next(); // after second yield
const fourthResult = testObj.next(); // after third yield

yield 委托迭代

*yield 组合使用,可以将多个生成器连接在一起

function * anotherGenerator(i) {
  yield i + 1;
  yield i + 2;
  yield i + 3;
}

function * generator(i) {
  yield* anotherGenerator(i);
}

var gen = generator(1);

gen.next().value; // 2
gen.next().value; // 3
gen.next().value; // 4

类型判断

业界常见的判断 Generator 对象和函数的方法

如何判断 Generator 对象

function isGenerator(obj) {
    return obj && typeof obj.next === 'function' && typeof obj.throw === 'function';
}

这里运用鸭子模型进行判断
如果对象中有 nextthrow 两个方法,那么就认为这个对象是一个生成器对象

如何判断 Generator 函数

function isGeneratorFunction(obj){
    var constructor = obj.constructor;
    if(!constructor) return false;
    if(
        'GeneratorFunction' === constructor.name ||
        'GeneratorFunction' === constructor.displayName
    ) return true;
    return isGenerator(constructor.prototype);
}

利用函数的 constructor 构造器的名字来判断(name 与 displayName 为了处理兼容性)
这里递归调用 isGenerator 判断 constructor 的原型是因为有自定义迭代器的存在

总结

Generator 函数作为一种新的状态机制管理,让一段代码逻辑可以动态控制分段执行,有独到的作用和使用场景

但如果独立使用,操作繁琐,语义也不清晰
所以在 ES6 引入后,社区使用例如 co 库进行二次封装

在前端异步编程领域,开阔思路的作用大于实际应用,有点昙花一现的意思
为 Async/Await 做铺垫

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,080评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,422评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,630评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,554评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,662评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,856评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,014评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,752评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,212评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,541评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,687评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,347评论 4 331
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,973评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,777评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,006评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,406评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,576评论 2 349

推荐阅读更多精彩内容