根本不常用知识之Generator
首先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 执行后返回,带有 next、return、throw 等原型方法
- function* gen() {}
- const gObj = gen();
- gObj.next();
- gObj.return();
- 可通过
function*
语法来定义,它是 GeneratorFunction 的实例
- Object.getPrototypeOf(gen).constructor // GeneratorFunction {prototype: Generator, ...}
- 内置函数,但没有直接挂到 window 上,但可以通过实例来获取
- const GeneratorFunction = Object.getPrototypeOf(gen).constructor;
- 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 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 的执行过程的功能
以下代码基本都是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*???
- 状态实现不难,其实就是用一个flag标志记录状态在一定的时候去执行
- 状态是由用户层面代码生成,里面使用
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();
- }
- }
- }
- 可知每次 yield 对应着一个 switch case,每次都会 return,自然每次 yield 完后就“卡住了”
- 由 case return 可知 Generator 让权,就是主动执行别的 Generator,并退出自己的状态
- 同理 foo Generator 也是 switch case 这种结构
- 先看一下 babel 是如何编译 async 函数的,可以看到
_asyncToGenerator
,这其实就是自动执行。其次可以大概猜出regeneratorRuntime.mark
函数返回的其实就是 polyfill 的 Generator - 所以 foo 执行 switch 完,经过一些内部操作把
{ value: "wangshun", done: true }
作为了 mark 函数的返回值,并交给 _asyncToGenerator 使用,_asyncToGenerator调用promise.then(next)
继续执行 - 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 函数其实就是接受一个函数并改变成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;
- }
- 每个 wrap 会创建一个 context 来管理状态以及上下文参数,每次执行 case 时会先打个快照,防止 yield 完后参数更改
- 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);
- }
- }
上面提到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的更多相关文章
- 【基于WPF+OneNote+Oracle的中文图片识别系统阶段总结】之篇一:WPF常用知识以及本项目设计总结
篇一:WPF常用知识以及本项目设计总结:http://www.cnblogs.com/baiboy/p/wpf.html 篇二:基于OneNote难点突破和批量识别:http://www.cnblog ...
- javascript常用知识点集
javascript常用知识点集 目录结构 一.jquery源码中常见知识点 二.javascript中原型链常见的知识点 三.常用的方法集知识点 一.jquery源码中常见的知识点 1.string ...
- AngularJS进阶(十二)AngularJS常用知识汇总(不断更新中....)
AngularJS常用知识汇总(不断更新中....) 注:请点击此处进行充电! app.controller('editCtrl',['$http','$location','$rootScope', ...
- 打造自己的Android常用知识体系
前言 Android常用知识体系是什么鬼?所谓常用知识体系,就是指对项目中重复使用率较高的功能点进行梳理.注意哦,不是Android知识体系. 古语道:学而不思则罔,思而不学则殆.如果将做项目类比为“ ...
- jQuery常用知识总结
jQuery常用知识总结 简介 选择器 属性操作 jQuery() each event事件 jQuery扩展 一.简介 What is jQuery jQuery is fast small and ...
- Python数据分析与挖掘所需的Pandas常用知识
Python数据分析与挖掘所需的Pandas常用知识 前言Pandas基于两种数据类型:series与dataframe.一个series是一个一维的数据类型,其中每一个元素都有一个标签.series ...
- Oracle常用知识小总结
永不放弃,一切皆有可能!!! 只为成功找方法,不为失败找借口! Oracle常用知识小总结 1. 创建自增主键 对于习惯了SQL SERVER的图形化界面操作的SQLer,很长一段时间不用oracle ...
- php常用知识集锦
php常用知识集锦 很多位置都有写好的代码,自己做项目的时候可以直接拿来用,而不用自己写,比如现在看到的菜鸟教程. 1.判断是否为空 empty($_POST["name"]) 2 ...
- javascript常用知识汇总
javascript这个语言庞大而复杂,我用了三年多了,还是皮毛都不会.从刚开始的jquery,到后来的es6,每天都在学习,每天都在忘记. 1.禁止手机虚拟键盘弹出 在开发适配手机的页面时,出现了这 ...
- Hadoop入门 集群常用知识与常用脚本总结
目录 集群常用知识与常用脚本总结 集群启动/停止方式 1 各个模块分开启动/停止(常用) 2 各个服务组件逐一启动/停止 编写Hadoop集群常用脚本 1 Hadoop集群启停脚本myhadoop.s ...
随机推荐
- mxArray 和 mwArray 的区别
首先,mxArray是MatlabC 函数库的结构体,而mwArray是Matlab C++ 函数库中对mxArray的包装 类. 其次,二者的内存管理方式不同.mxArray的内存管理方式比较松散, ...
- Windows10 WSL开启SSH登录
1.wsl 安装ssh服务(使用的是ubuntu)sudo apt install openssh-server 2.修改配置文件sudo vim /etc/ssh/sshd_config关键的几处修 ...
- shell语法4-printf命令、test命令和判断符号[]、逻辑运算符&&和||
一.printf命令 printf命令用于格式化输出,类似于C/C++中的printf函数. 默认不会在字符串末尾添加换行符!!! 例如: printf "%10d.\n" 123 ...
- Drift Programming | 漂移编程 | 哲学编程 | 架构设计 | TDD |DDD |Microservice
- java将Word转换成PDF方法
转载1:java将Word转换成PDF三种方法_pdfoptions_Zhsh-7的博客-CSDN博客 转载2:POI 实现 word转成pdf - 挽留匆匆的美丽 - 博客园 (cnblogs.co ...
- NLP学习日记
数据读取 下载csv文件后使用excel进行转存,然后用pandas读取,再把读取后转为numpy,numpy的tensor里.-1代表数组的最大维度,将原始数据集的标签和特征集分开,便于下一步的处理 ...
- Mac 启动转换助理 安装 win10
Mac 启动转换助理 安装 win10 Mac的处理器 是Inter芯 才有该功能 打开启动转换助理/Bootcamp 设置分区大小 win10安装完成后 在左侧的此电脑中找到咱们的Boot Camp ...
- 关于htpasswd
什么是 htpasswd htpasswd是一个apache的内置工具,其生成的文件称之为htpasswd文件.htpasswd文件本身一个密码本,或者类似于数据库一样,用来存储一些密码(凭证)信息. ...
- jmeter压测dubbo接口,参数为dto时如何写传参及有错误时的分析思路
一.传参 1. 无论dubbo接口传参是否为dto,所有参数都是在args的tab传进去的. 2. 如果dto中有自定义对象,paramType为自定义dto名,paramValue为其他参数组成的j ...
- java中post推送json格式字符串
最近项目中遇到post推送json格式字符串,之前写过推送json数据,调用失败,才发现是直接推送字符串,只不过字符串是json的格式. 在postman中调用如下: Java中代码如下: /** * ...