首先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库 (以前看这个库我是真心觉得不错,代码量还不大)

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

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

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

  1. class GeneratorFunction {}
  2. // GeneratorFunction 的 prototype 很通用,单独拎出来
  3. class GeneratorFunctionPrototype {
  4. static [Symbol.toStringTag] = "GeneratorFunction";
  5. // 实现 iterator protocol
  6. next(args) {}
  7. return(args) {}
  8. throw(args) {}
  9. // 实现 iterable protocol
  10. [Symbol.iterator]() {
  11. return this;
  12. }
  13. }
  14. // 相互引用
  15. GeneratorFunctionPrototype.constructor = GeneratorFunction;
  16. GeneratorFunction.prototype = GeneratorFunctionPrototype;
  17.  
  18. // 作用不大,设置 prototype 即可
  19. //class Generator {}
  20. //Generator.prototype = GeneratorFunctionPrototype.prototype;
async/await 和 Generator函数区别
  1. async function a() {}
  2.  
  3. function* b() {}
  4.  
  5. // babel 编译后
  6. function asyncGeneratorStep(gen, resolve, reject, _next, ...) {
  7. // 调用 gen 的 next 或 throw 方法
  8. var info = gen[key](arg);
  9. var value = info.value;
  10. if (info.done) {
  11. resolve(value);
  12. } else {
  13. Promise.resolve(value).then(_next, _throw);
  14. }
  15. }
  16. function _asyncToGenerator(fn) {
  17. return function () {
  18. return new Promise(function (resolve, reject) {
  19. // 获取 generator 对象
  20. var gen = fn.apply(self, arguments);
  21. function _next(value) {
  22. asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value);
  23. }
  24. // 初始化执行 next
  25. _next(undefined);
  26. });
  27. };
  28. }

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

 
Generator 深入探索

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

  1. let num = 0;
  2. async function gen() {
  3. num = num + (await wait(10));
  4. // num = (await wait(10)) + num;
  5. await foo();
  6. return num;
  7. }
  8.  
  9. async function foo() {
  10. await "wangshun";
  11. }
  12.  
  13. (async () => {
  14. // debugger;
  15. await gen();
  16. console.log("ws: res", num);
  17. })();
一、问题

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

二、状态
  1. 状态实现不难,其实就是用一个flag标志记录状态在一定的时候去执行
  2. 状态是由用户层面代码生成,里面使用 switch case + context 记录参数 实现
  1. function _callee$(_context) {
  2. while (1) {
  3. switch (_context.next) {
  4. case 0:
  5. // await wait(10)
  6. _context.next = 3;
  7. return wait(10);
  8. case 3:
  9. // await 123
  10. _context.next = 7;
  11. return 123;
  12. case 7:
  13. _context.next = 9;
  14. // await foo()
  15. return foo();
  16. case "end":
  17. return _context.stop();
  18. }
  19. }
  20. }
  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
  1. function _foo() {
  2. _foo = _asyncToGenerator(
  3. regeneratorRuntime.mark(function _callee2() {
  4. return regeneratorRuntime.wrap(function _callee2$(_context2) {
  5. switch (_context2.next) {
  6. case 0:
  7. _context2.next = 2;
  8. return "literal";
  9. case "end":
  10.  
  11. return _context2.stop();
  12. }
  13. }, _callee2);
  14. })
  15. );
  16. return _foo.apply(this, arguments);
  17. }

总结一下,父级 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
  1. function mark(genFn: () => void) {
  2. return _inheritsLoose(genFn, GeneratorFunctionPrototype);
  3. }
  4. function _inheritsLoose(subClass, superClass) {
  5. Object.setPrototypeOf(subClass, superClass);
  6. subClass.prototype = Object.create(superClass.prototype);
  7. subClass.prototype.constructor = subClass;
  8. return subClass;
  9. }
  1. 每个 wrap 会创建一个 context 来管理状态以及上下文参数,每次执行 case 时会先打个快照,防止 yield 完后参数更改
  2. mark 函数的 next、return、throw 最终调用是 wrap 的能力,因为实际是 wrap 是调用(switch case)和 context,所以GeneratorFunctionPrototype里用_invoke链接warp,自己只负责传递 type 和 args
  1. type GeneratorMethod = "next" | "return" | "throw";
  2. class GeneratorFunctionPrototype {
  3. private _invoke: (method: GeneratorMethod, args) => { value: any, done: boolean };
  4. // 注意这是原型方法哦
  5. next(args) {
  6. return this._invoke("next", args);
  7. }
  8. return(args) {
  9. return this._invoke("return", args);
  10. }
  11. throw(args) {
  12. return this._invoke("throw", args);
  13. }
  14.  
  15. }
五、yield*

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

  1. async function a() {
  2. const res = await b();
  3. }
  4.  
  5. async function b() {
  6. await 1;
  7. await 'str';
  8. return { data: 'lawler', msg: 'ok' };
  9. }

上面的代码可以验证

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

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

wrap 里面,循环执行

  1. generator._invoke = function invoke(method, args) {
  2. context.method = method;
  3.  
  4. // yield* genFn 时使用,循环返回 genFn 迭代的结果,直到 return
  5. while (true) {
  6. const delegate = context.delegate;
  7. if (delegate) {
  8. const delegateResult = maybeInvokeDelegate(delegate, context);
  9. if (delegateResult) {
  10. if (delegateResult === empty) continue;
  11. // 传出内部迭代结果 { value, done }
  12. return delegateResult;
  13. }
  14. }
  15. }
  16.  
  17. if (method === "next") {}
  18. }

根本不常用知识之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. mxArray 和 mwArray 的区别

    首先,mxArray是MatlabC 函数库的结构体,而mwArray是Matlab C++ 函数库中对mxArray的包装 类. 其次,二者的内存管理方式不同.mxArray的内存管理方式比较松散, ...

  2. Windows10 WSL开启SSH登录

    1.wsl 安装ssh服务(使用的是ubuntu)sudo apt install openssh-server 2.修改配置文件sudo vim /etc/ssh/sshd_config关键的几处修 ...

  3. shell语法4-printf命令、test命令和判断符号[]、逻辑运算符&&和||

    一.printf命令 printf命令用于格式化输出,类似于C/C++中的printf函数. 默认不会在字符串末尾添加换行符!!! 例如: printf "%10d.\n" 123 ...

  4. Drift Programming | 漂移编程 | 哲学编程 | 架构设计 | TDD |DDD |Microservice

  5. java将Word转换成PDF方法

    转载1:java将Word转换成PDF三种方法_pdfoptions_Zhsh-7的博客-CSDN博客 转载2:POI 实现 word转成pdf - 挽留匆匆的美丽 - 博客园 (cnblogs.co ...

  6. NLP学习日记

    数据读取 下载csv文件后使用excel进行转存,然后用pandas读取,再把读取后转为numpy,numpy的tensor里.-1代表数组的最大维度,将原始数据集的标签和特征集分开,便于下一步的处理 ...

  7. Mac 启动转换助理 安装 win10

    Mac 启动转换助理 安装 win10 Mac的处理器 是Inter芯 才有该功能 打开启动转换助理/Bootcamp 设置分区大小 win10安装完成后 在左侧的此电脑中找到咱们的Boot Camp ...

  8. 关于htpasswd

    什么是 htpasswd htpasswd是一个apache的内置工具,其生成的文件称之为htpasswd文件.htpasswd文件本身一个密码本,或者类似于数据库一样,用来存储一些密码(凭证)信息. ...

  9. jmeter压测dubbo接口,参数为dto时如何写传参及有错误时的分析思路

    一.传参 1. 无论dubbo接口传参是否为dto,所有参数都是在args的tab传进去的. 2. 如果dto中有自定义对象,paramType为自定义dto名,paramValue为其他参数组成的j ...

  10. java中post推送json格式字符串

    最近项目中遇到post推送json格式字符串,之前写过推送json数据,调用失败,才发现是直接推送字符串,只不过字符串是json的格式. 在postman中调用如下: Java中代码如下: /** * ...