首先babel链接很重要

https://www.babeljs.cn/repl#?browsers=&build=&builtIns=false&corejs=3.6&spec=false&loose=false&code_lz=FAAhBsFMBcQOwK4FsQF4QAYDcwCQAzBOAY2gEsB7OEAKhAHcBnAc0moAoBKAb1DH4CeZSOAAmdANoBGADQgATAF0c_EAF8-AQ0YCSIQiXJUQrOFxC9ViFOmsgA1CHab6msrFfv2UjJ04r-AHpA-GQ0Jxc3DyjvX04HUKQAsEj3fQoKLmSQACcYBBzqawCNLR09A1JKanwM80t-VNgAInB3SBzNcGaS4D5ncuIneNQAPgs-MGCQUUgAIwRmVhzsppM2LMmQYipGCigAOnAKZnZmpgAuXMhGZrlrfz41Tk3gIA&debug=false&forceAllTransforms=false&shippedProposals=false&circleciRepo=&evaluate=true&fileSize=false&timeTravel=false&sourceType=module&lineWrap=true&presets=env%2Creact%2Cstage-1%2Cstage-2%2Cstage-3&prettier=true&targets=&version=7.18.12&externalPlugins=&assumptions=%7B%7D

看这个之前 其实可以先去了解一下co库 (以前看这个库我是真心觉得不错,代码量还不大)

不想看库的话 请看下面简易版

function isPromise(obj: any) {
return typeof obj.then === "function";
}
function isGenerator(obj: any) {
return typeof obj.next === "function" && typeof obj.throw === "function";
}
function isGeneratorFunction(obj: any) {
const { constructor } = obj;
if (!constructor) return false;
if ([constructor.name, constructor.displayName].includes("GeneratorFunction"))
return true;
return isGenerator(constructor.prototype);
}
//上面三个是co库原生的校验
export default function co(gen) {
return new Promise((resolve, _reject) => {
typeof gen === "function" && (gen = gen()); function next(data) {
const ret = gen.next(data);
if (ret.done) return resolve(ret.value);
toPromise(ret.value).then(next);
} function toPromise(obj) {
if (isPromise(obj)) return obj;
if (isGeneratorFunction(obj) || isGenerator(obj)) return co(obj);
// if (other types) {}
return; // error
} next();
});
}
Generator对象
一、generator Object
  1. 由 Generator 执行后返回,带有 next、return、throw 等原型方法
function* gen() {}
const gObj = gen();
gObj.next();
gObj.return();
二、Generator
  1. 可通过 function* 语法来定义,它是 GeneratorFunction 的实例
Object.getPrototypeOf(gen).constructor // GeneratorFunction {prototype: Generator, ...}
三、GeneratorFunction
  1. 内置函数,但没有直接挂到 window 上,但可以通过实例来获取
const GeneratorFunction = Object.getPrototypeOf(gen).constructor;
  1. GeneratorFunction 和 Function 是一个级别的,可以传参来创建函数,如
const gen = new GeneratorFunction('a', 'yield a * 2');
四、三者之间的关系

如果对原型链和继承记不清了先看看原型链与继承

class GeneratorFunction {}
// GeneratorFunction 的 prototype 很通用,单独拎出来
class GeneratorFunctionPrototype {
static [Symbol.toStringTag] = "GeneratorFunction";
// 实现 iterator protocol
next(args) {}
return(args) {}
throw(args) {}
// 实现 iterable protocol
[Symbol.iterator]() {
return this;
}
}
// 相互引用
GeneratorFunctionPrototype.constructor = GeneratorFunction;
GeneratorFunction.prototype = GeneratorFunctionPrototype; // 作用不大,设置 prototype 即可
//class Generator {}
//Generator.prototype = GeneratorFunctionPrototype.prototype;
async/await 和 Generator函数区别
async function a() {}

function* b() {}

// babel 编译后
function asyncGeneratorStep(gen, resolve, reject, _next, ...) {
// 调用 gen 的 next 或 throw 方法
var info = gen[key](arg);
var value = info.value;
if (info.done) {
resolve(value);
} else {
Promise.resolve(value).then(_next, _throw);
}
}
function _asyncToGenerator(fn) {
return function () {
return new Promise(function (resolve, reject) {
// 获取 generator 对象
var gen = fn.apply(self, arguments);
function _next(value) {
asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value);
}
// 初始化执行 next
_next(undefined);
});
};
}

说白了async/await 也就是co库的原生处理 自动执行Generator 但是对比Generator删减了如何监听另一个 Generator 的执行过程的功能

 
Generator 深入探索

以下代码基本都是babel的简写或粘贴过来的直接看babel编译过的也一样

  let num = 0;
async function gen() {
num = num + (await wait(10));
// num = (await wait(10)) + num;
await foo();
return num;
} async function foo() {
await "wangshun";
} (async () => {
// debugger;
await gen();
console.log("ws: res", num);
})();
一、问题

Generator 的状态是如何实现的换句话说 Generator 是怎么做到 yield 结束就停止的????Generator是如何让权给另一个 Generator,之后又让权回来的???一个 Generator 是如何监听另一个 Generator 的执行过程,即 yield*???

二、状态
  1. 状态实现不难,其实就是用一个flag标志记录状态在一定的时候去执行
  2. 状态是由用户层面代码生成,里面使用 switch case + context 记录参数 实现
function _callee$(_context) {
while (1) {
switch (_context.next) {
case 0:
// await wait(10)
_context.next = 3;
return wait(10);
case 3:
// await 123
_context.next = 7;
return 123;
case 7:
_context.next = 9;
// await foo()
return foo();
case "end":
return _context.stop();
}
}
}
  1. 可知每次 yield 对应着一个 switch case,每次都会 return,自然每次 yield 完后就“卡住了”
三、协作
  1. 由 case return 可知 Generator 让权,就是主动执行别的 Generator,并退出自己的状态
  2. 同理 foo Generator 也是 switch case 这种结构
  3. 先看一下 babel 是如何编译 async 函数的,可以看到_asyncToGenerator,这其实就是自动执行。其次可以大概猜出 regeneratorRuntime.mark 函数返回的其实就是 polyfill 的 Generator
  4. 所以 foo 执行 switch 完,经过一些内部操作把 { value: "wangshun", done: true } 作为了 mark 函数的返回值,并交给 _asyncToGenerator 使用,_asyncToGenerator调用 promise.then(next)继续执行
  5. gen 函数也是这样,等待 foo resolve,然后 gen 返回 { value: xxx, done: false },继续 next
function _foo() {
_foo = _asyncToGenerator(
regeneratorRuntime.mark(function _callee2() {
return regeneratorRuntime.wrap(function _callee2$(_context2) {
switch (_context2.next) {
case 0:
_context2.next = 2;
return "literal";
case "end": return _context2.stop();
}
}, _callee2);
})
);
return _foo.apply(this, arguments);
}

总结一下,父级 gen 函数执行到一个 case,将子 foo 函数的返回值作为本次结果,然后将自己卡住(其实就是在 co 层面等待子 promise resolve), foo 执行完后返回 done true,并结束自己的状态生涯,再将自己 co 层面的 Promise resolve,gen 卡住的 Promise 收到了 foo 的结果,本次返回 done false,开启下一轮 next,并重新通过 context.next 进入到对应 case 中

四、mark、wrap、Context
  1. mark 函数其实就是接受一个函数并改变成Generator其本质就是继承GeneratorFunctionPrototype
function mark(genFn: () => void) {
return _inheritsLoose(genFn, GeneratorFunctionPrototype);
}
function _inheritsLoose(subClass, superClass) {
Object.setPrototypeOf(subClass, superClass);
subClass.prototype = Object.create(superClass.prototype);
subClass.prototype.constructor = subClass;
return subClass;
}
  1. 每个 wrap 会创建一个 context 来管理状态以及上下文参数,每次执行 case 时会先打个快照,防止 yield 完后参数更改
  2. mark 函数的 next、return、throw 最终调用是 wrap 的能力,因为实际是 wrap 是调用(switch case)和 context,所以GeneratorFunctionPrototype里用_invoke链接warp,自己只负责传递 type 和 args
type GeneratorMethod = "next" | "return" | "throw";
class GeneratorFunctionPrototype {
private _invoke: (method: GeneratorMethod, args) => { value: any, done: boolean };
// 注意这是原型方法哦
next(args) {
return this._invoke("next", args);
}
return(args) {
return this._invoke("return", args);
}
throw(args) {
return this._invoke("throw", args);
} }
五、yield*

上面提到await、async 舍弃 Generator 是监听到另一个 Generator 的执行过程,也就是说使用await过程中并不知道执行了多少await

async function a() {
const res = await b();
} async function b() {
await 1;
await 'str';
return { data: 'lawler', msg: 'ok' };
}

上面的代码可以验证

但是 yield 是通过 delegateYield 方法接替了 foo,在 context 内部循环运行,使得这次 yield 在一个 case 中完成(可以在babel中写一个yield genFn()试一下就能看出来)

function gen$(_context) {
switch (_context.next) {
case 0:
_context.next = 7;
return wait(10);
case 7:
// 传递 foo generator object 给 gen 的 context
return _context.delegateYield(foo(), "t2", 8);
case "end":
return _context.stop();
}
}

wrap 里面,循环执行

generator._invoke = function invoke(method, args) {
context.method = method; // yield* genFn 时使用,循环返回 genFn 迭代的结果,直到 return
while (true) {
const delegate = context.delegate;
if (delegate) {
const delegateResult = maybeInvokeDelegate(delegate, context);
if (delegateResult) {
if (delegateResult === empty) continue;
// 传出内部迭代结果 { value, done }
return delegateResult;
}
}
} if (method === "next") {}
}

根本不常用知识之Generator的更多相关文章

  1. 【基于WPF+OneNote+Oracle的中文图片识别系统阶段总结】之篇一:WPF常用知识以及本项目设计总结

    篇一:WPF常用知识以及本项目设计总结:http://www.cnblogs.com/baiboy/p/wpf.html 篇二:基于OneNote难点突破和批量识别:http://www.cnblog ...

  2. javascript常用知识点集

    javascript常用知识点集 目录结构 一.jquery源码中常见知识点 二.javascript中原型链常见的知识点 三.常用的方法集知识点 一.jquery源码中常见的知识点 1.string ...

  3. AngularJS进阶(十二)AngularJS常用知识汇总(不断更新中....)

    AngularJS常用知识汇总(不断更新中....) 注:请点击此处进行充电! app.controller('editCtrl',['$http','$location','$rootScope', ...

  4. 打造自己的Android常用知识体系

    前言 Android常用知识体系是什么鬼?所谓常用知识体系,就是指对项目中重复使用率较高的功能点进行梳理.注意哦,不是Android知识体系. 古语道:学而不思则罔,思而不学则殆.如果将做项目类比为“ ...

  5. jQuery常用知识总结

    jQuery常用知识总结 简介 选择器 属性操作 jQuery() each event事件 jQuery扩展 一.简介 What is jQuery jQuery is fast small and ...

  6. Python数据分析与挖掘所需的Pandas常用知识

    Python数据分析与挖掘所需的Pandas常用知识 前言Pandas基于两种数据类型:series与dataframe.一个series是一个一维的数据类型,其中每一个元素都有一个标签.series ...

  7. Oracle常用知识小总结

    永不放弃,一切皆有可能!!! 只为成功找方法,不为失败找借口! Oracle常用知识小总结 1. 创建自增主键 对于习惯了SQL SERVER的图形化界面操作的SQLer,很长一段时间不用oracle ...

  8. php常用知识集锦

    php常用知识集锦 很多位置都有写好的代码,自己做项目的时候可以直接拿来用,而不用自己写,比如现在看到的菜鸟教程. 1.判断是否为空 empty($_POST["name"]) 2 ...

  9. javascript常用知识汇总

    javascript这个语言庞大而复杂,我用了三年多了,还是皮毛都不会.从刚开始的jquery,到后来的es6,每天都在学习,每天都在忘记. 1.禁止手机虚拟键盘弹出 在开发适配手机的页面时,出现了这 ...

  10. Hadoop入门 集群常用知识与常用脚本总结

    目录 集群常用知识与常用脚本总结 集群启动/停止方式 1 各个模块分开启动/停止(常用) 2 各个服务组件逐一启动/停止 编写Hadoop集群常用脚本 1 Hadoop集群启停脚本myhadoop.s ...

随机推荐

  1. win系统airtest+pytest-xdist服务器分布式运行。

    1.准备至少两台服务器,集群全部是局域网,(启动脚本的时候可以使用外网ip). 2.输出的报告地址,需要把文件夹设置成共享文件夹,(连接的时候使用内外ip). 启动脚本文件 import os, da ...

  2. Canvas布局下使用附加属性使控件岁鼠标移动

    定义附加属性 public class MoveBehavior { public static readonly DependencyProperty IsMoveAbleProperty = De ...

  3. 分布式计算 Distributed computing

    分布式计算(Distributed computing),又译为分散式运算,是一门计算机科学,它研究如何把一个需要非常巨大的计算能力才能解决的问题分成许多小的部分,然后把这些部分分配给许多计算机进行处 ...

  4. TCAM and CAM memory usage inside networking devices(转)

    TCAM and CAM memory usage inside networking devices Valter Popeskic Equipment and tools, Physical la ...

  5. 【坑】 centos 7.6 使用ssh 秘钥登录遇到的问题

    问题: 我在master1上面创建了一个ssh的秘钥文件,通过ssh-copy-id 传送四台主机,然后使用ansible all -m ping 发现有台机器就是有问题 这是怎么回事???一样的配置 ...

  6. React 使用链表遍历组件树

    React 为在有限的资源情况下,更好地控制UI的更新,提出了时间分片的概念.以达到三个目标: performing non-blocking rendering(无阻塞渲染):applying up ...

  7. 230219 Business 31-48

    31.  31: Maternity LeaveVeronica, when is your baby due?Next month.Are you going on maternity leave? ...

  8. 给自己提个醒,渲染模版引擎handlebars已经足够好用了,不要再到处乱看浪费时间了。

    <html><body onload="renderHello()"><div id="target">Loading... ...

  9. Win10下VM虚拟机桥接模式无法上网的相关问题?

    首先:在LR中点击虚拟机->设置,勾选桥接模式,勾选之后无法上网 1.    选中虚拟网卡VMnet8(NAT模式)和WLAN(本机)网卡,右键选择"桥接",然后系统创建一个 ...

  10. java script--一些冷知识

    java script--一些冷知识 目录 java script--一些冷知识 布兰登·艾奇,JavaScript 创始人 java script logo json logo json node. ...