深入了解 Generators

随着JavaScript语言的发展,ES6规范带来了许多内容, 其中生成器Generators是一项重要的特性。 利用这一特性,可以简化迭代器的创建, 更加令人兴奋的是Generators允许在函数执行过程中暂停、并在将来某一时刻恢复执行。 这一特性改变了以往函数必须执行完成才返回的特点, 将这一特性应用到异步编码中, 可以有效的简化异步方法的写法,同时避免陷入回调地狱。

Generators 介绍

一个简单的Generator函数示例:

function* fn() {
  yield 1;
  yield 2;
  yield 3;
}
var fun = fn();
fun.next(); // {value: 1, done: false}
fun.next(); // {value: 2, done: false}
fun.next(); // {value: 3. done: false}
fun.next();// {value: undefiend, done: true}

上述代码中定义了一个生成器函数, 当调用生成器函数 fn()时, 并不是立即执行该函数, 而是返回一个生成器对象。 每当调用生成器对象的.next()方法时, 函数将运行到下一个yield表达式, 返回表达式的结果并暂停自身。 当抵达生成器函数的末尾时, 返回结果中done的值变为true, value的值为undefiend。我们将上述fn()函数称之为生成器函数, 与普通函数相比二者有如下区别:

  • 普通函数使用function声明, 生成器函数用function*声明
  • 普通函数使用 return 返回值, 生成器函数使用 yield返回值
  • 普通函数是 run to completion模式, 即普通函数开始执行后, 会一直执行函数所有语句执行完成,在此期间别的代码语句是不会被执行的; 而生成器函数是run-pause-run模式, 即生成器函数可以在函数运行中被暂停一次或多次, 并且在后面再恢复执行,在暂停期间允许其他代码语句被执行。

yield在JavaScript中如何实现的呢?

首先,生成器不是线程,支持线程的语言中,多端代码可以同一时间运行, 这经常会导致资源竞争, 使用得当会有不错的性能提升。 生成器则完全不同, JavaScript执行引擎仍然是一个基于事件循环机制的单线程环境。 当生成器运行的时候, 它会在叫做caller的同一线程中运行。 执行的顺序是有序、确定的, 并且永远不会产生并发。 不同于系统的线程, 生成器只会在其内部用到 yield的时候才会被挂起。

通过babel将生成器函数转换为ES5:

function* fn() {
  yield 1;
  yield 2;
  yield 3;
}
var fun=fn();
fun.next();

\color{red}{转化ES5后:}

"use strict";
var _marked = /*#__PURE__*/regeneratorRuntime.mark(fn);
function fn() {
  return regeneratorRuntime.wrap(function fn$(_context) {
    while (1) {
      switch (_context.prev = _context.next) {
        case 0:
          _context.next = 2;
          return 1;
        case 2:
          _context.next = 4;
          return 2;
        case 4:
          _context.next = 6;
          return 3;
        case 6:
        case "end":
          return _context.stop();
      }
    }
  }, _marked);
}
var fun = fn();
fun.next();

从转换后的代码中可以看到, yield表达值转换时, Regenerator将生成器函数中的yield表达式重写为 switch case, 同时在每个case中使用context$1$0来保存函数当前的上下文状态。
switch case之外, 迭代器函数fn被regeneratorRuntime.mark包装, 返回一个被regeneratorRuntime.wrap包装的迭代器对象。

生成器函数的运行时Regenerator通过工具函数将生成器函数包装, 为其添加如next/return 等方法。同是也对返回的生成器对象进行包装, 使得对next等方法的调用, 最终进入由switch case 组成的状态机模型中。而保存生成器函数上下文信息则利用了闭包的技巧。

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

推荐阅读更多精彩内容