Javascript——生成器(Generator)自动执行

这篇具有很好参考价值的文章主要介绍了Javascript——生成器(Generator)自动执行。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、实现原理

1.1  原因及代码段

Generator自动化是通过Thunk函数进行实现,写这篇文章的目的是为了理解通过Thunk实现Generator函数的自动执行。

1.2 代入的业务场景

我们可以带入一个业务场景来帮助我们理解Thunk实现Generator自动执行的好处,业务场景如下:

假设小明今天干了一件事情是:

        1、买菜

        2、买完菜回家先做饭

        3、接着洗菜、切菜

        4、然后煮菜

        5、煮完菜等待饭好后,吃饭

1.3 实现Generator自动执行需要准备的函数

1.3.1 Thunk函数——通用版

概念:Thunk 函数替换的不是表达式,而是多参数函数,将其替换成单参数的版本,且只接受回调函数作为参数

1. 有且只有一个参数是callback(回调)的函数;

2. 原函数是多参数函数;(如果只有一个参数是没有改造的必要)

3. 通常情况下,callback的第一个参数是error

代码1(通用Thunk):


function Thunk(fn) {
    return function(...args) {
        return function(callback) {
            return fn.call(this,...args,callback)
        }
    }
}

代码2(使用Thunk,引入代码2只是为了介绍该封装的通用Thunk的使用规则,其它毫无作用):

function delay(time,dealCallback,callback) {
    setTimeout(() => {
        dealCallback();
        callback();
    }, time ? time : 1000);
}


const thunkDelay1 = Thunk(delay)(500,() => {
    console.log("执行了dealCallback")
});

thunkDelay1(() => {
  console.log("通过thunk执行了callback");
})

函数Thunk(使用了函数柯里化实现,函数柯里化可以去了解一下),上图中的thunk实现之后的调用规则实际上是:

① 第一层调用Thunk函数,传入需要执行的函数fn,如:Thunk(delay),此时我们就得到了function(...args) {...}

② 第二层传入需要执行的函数fn中的除callback的参数,如:const thunkDelay1 = Thunk(delay)(500,() => {...})

③ 第三层传入需要执行的callback,如:thunkDelay1(() => {...})

通过该thunk可以执行所有的:拥有多个参数,且只有一个参数为callback的函数。且执行规则必须是上面的①、②、③步骤,此刻看起来好像确实没有什么用,我们需要执行什么函数直接调不就行了?

1.3.2 准备一些需要执行的函数

上文中的delay其实就算是其中一种,不过我们也可以写多个需要执行的函数。这里为什么写的有些需要执行的函数不符合使用Thunk的初衷(拥有多个参数(不止一个),且只有一个callback参数,要是只有一个参数就没必要使用Thunk了:这里是为了再次强调Thunk本身需要满足的条件,因为如果我们改造的函数,像:buyVegetables、cookRice、washVegetables、cookVegetables,如果是只有一个callback参数,那么再后续的执行生成器的函数中(1.3.3)就没必要再使用Thunk修饰了,应该直接使用yield buyVegetables; ...,

// 1、买菜(假设去买菜到买菜到家花了1s钟)
function buyVegetables (callback) {
  console.log("小明出去买菜了");
  setTimeout(() => {
    console.log("买完菜到家了");
    callback();
  }, 1000)
}

// 2、煮饭
function cookRice(callback) {
  console.log("小明去煮饭了");
  setTimeout(() => {
    console.log("小明已经把米放进电饭煲,按下煮饭开关了");
    callack();
  }, 1000)
}

// 3、洗菜、切菜
function washVegetables(callback) {
  console.log("小明开始洗菜切菜");
  setTimeout(() => {
    console.log("小明洗菜、切菜");
    callback();    
  }, 1000)
}

// 4、煮菜
function cookVegetables(callback) {
  console.log("小明开始煮菜了");
  setTimeout(() => {
    console.log("美食已经烹饪完");
    callback();
  })
}

// 5、等待饭熟后吃饭
function eatRice(waitTime, waitHandleThings,callback) {
  setTimeout(() => {
    console.log("饭熟了");
    waitHandleThings();
    callback();
  }, waitTime ? waitTime : 1000)
}

1.3.3 准备一个执行某个功能的流程生成器:Generator

function *g() {
  yield Thunk(buyVegetables)();
  yield Thunk(cookRice)();
  yield Thunk(washVegetables)();
  yield Thunk(cookVegetables)();
  yield Thunk(eatRice)(() => {
    console.log("等待饭熟的过程中,吃了一个橘子");
  });
}

1.3.4 准备一个自动执行生成器的函数:thunkForGenerator

function thunkForGenerator(callback) {
  const g = callback();

  function next() {
    // 调用生成器的next()
    let result = g.next();
    // 生成器执行完了直接return
    if (result.done) {
      return;
    }
    /**
     *
     * result.value拿到yield后面返回的通过Thunk(fn)(...args)返回的包装的函数func(callback)
     * 包装函数最终执行 fn.call(this,...args,callback)
     */
    result.value(next);
  }
  next();
}

1.3.5 所需要准备的函数功能分析

①  Thunk函数:它的作用是自动控制Generator函数的流程,接收和交还程序的执行权;

②  需要执行流程的函数:都必须有一个callback参数,用以回调执行Generator中的.next()中的.value获得的函数

③  Generator生成器函数:这个函数主要是为了执行我们所需要执行的流程

④  thunkForGenerator这个函数要求g这个Generator函数里,每个函数里都是Thunk函数(这里我的理解是,Generator函数每一个yield后面执行的函数应该是都有一个callback参数,作为回调执行下一次Generator.next().value() );

二、全部代码

function Thunk(fn) {
  return function (...args) {
    return function (callback) {
      fn.call(this, ...args, callback);
    };
  };
}

// 1、买菜(假设去买菜到买菜到家花了1s钟)
function buyVegetables(callback) {
  console.log("小明出去买菜了");
  setTimeout(() => {
    console.log("买完菜到家了");
    callback();
  }, 1000);
}

// 2、煮饭
function cookRice(callback) {
  console.log("小明去煮饭了");
  setTimeout(() => {
    console.log("小明已经把米放进电饭煲,按下煮饭开关了");
    callback();
  }, 1000);
}

// 3、洗菜、切菜
function washVegetables(callback) {
  console.log("小明开始洗菜切菜");
  setTimeout(() => {
    console.log("小明洗菜、切菜");
    callback();
  }, 1000);
}

// 4、煮菜
function cookVegetables(callback) {
  console.log("小明开始煮菜了");
  setTimeout(() => {
    console.log("美食已经烹饪完");
    callback();
  });
}

// 5、等待饭熟后吃饭
function eatRice(waitTime, waitHandleThings, callback) {
  setTimeout(
    () => {
      console.log("饭熟了");
      waitHandleThings();
      callback();
    },
    waitTime ? waitTime : 1000
  );
}

// 生成器方法
function* g() {
  yield Thunk(buyVegetables)();
  yield Thunk(cookRice)();
  yield Thunk(washVegetables)();
  yield Thunk(cookVegetables)();
  yield Thunk(eatRice)(500,() => {
    console.log("等待饭熟的过程中,吃了一个橘子");
  });
}

function thunkForGenerator(callback) {
  const g = callback();

  function next() {
    // 调用生成器的next()
    let result = g.next();
    // 生成器执行完了直接return
    if (result.done) {
      return;
    }
    /**
     *
     * result.value拿到yield后面返回的通过Thunk(fn)(...args)返回的包装的函数func(callback)
     * 包装函数最终执行 fn.call(this,...args,callback)
     */
    result.value(next);
  }
  next();
}
thunkForGenerator(g);

2.1 代码在执行时干了什么事

1. 代码执行过程

执行thunkForGenerator(g):

  ① const g = callback()将生成器赋值给了g;

  ② 接着执行了函数next(),函数next中将 g.next()赋值给了result,此时result拿到了:

        {

                vlaue: Thunk(buyVegetables)(),

                done: false

        }

        等价于

        {

                value: (callback) => {

                        buyVegetables.call(this,callback)

                },

                done: false

        }

接着执行result.value(next),实际等价于执行上面的.value方法,顺便将next作为回调函数callback传递进去,执行到回调函数next之后,又会通过g.next()拿到下一个yield后面的函数,如此循环反复,直到yield已经都被执行完,再次通过g.next()拿到的是 :

        {

                value: undefined,

                done: true

        }

此时,通过判断:

        if (result.done) {

                return

        }

中断后续的行为,因为已经执行完成了

2.控制权的交还流程如下:

虽然thunkForGenerator函数将next方法传给了Thunk函数,但该函数不会立即执行,而是传给了delay函数的参数三;
而delay函数的参数三是一个函数,但该函数只会在异步执行完毕后去执行;
也就是说,当第一次执行遍历器的next方法后,控制权从thunkForGenerator函数交给了g函数;
然后g函数会触发一次1000ms延迟的异步(thunkForAsync函数),等异步执行完毕后,g函数执行了thunkForGenerator函数的next方法,将控制权重新还给了他;
然后thunkForGenerator函数发现done为false,于是又执行了遍历器的next方法,将控制权交给了g函数;
g函数执行完代码,通过执行thunkForGenerator函数的next方法,将控制权还了回去;
此时thunkForGenerator函数再次执行遍历器的next,但g函数发现此时没代码可执行了,于是返回结果,done为true;
因为done为true,所以thunkForGenerator函数不再继续下去,也返回了。文章来源地址https://www.toymoban.com/news/detail-843326.html

到了这里,关于Javascript——生成器(Generator)自动执行的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

原文地址:https://blog.csdn.net/weixin_43893483/article/details/136905223

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 代码生成器-mybatis-plus-generator

    我们平时在开发的过程中,对于新建的一张表难免会有对其进行增删改查的操作,而且还要写Controller、service、Mapper、Mapper.xml、PO、VO等等。如果每次都要去写这些跟业务毫不相干但是却又耗时耗力的重复代码这不仅是让开发人员不能专注于业务逻辑甚至可能由于不注意导致字

    2023年04月25日
    浏览(7)
  • Python中的列表生成式(List Comprehensions)和列表生成器(Generator)

    Python中的列表生成式(List Comprehensions)和列表生成器(Generator),是Python提供的两个高级应用机制。 生成式是一种简写机制,坚持了龟叔的“Python要简单优雅”的设计理念。 生成器虽然翻译成中文以后只有一字之差,但是实现的机制和生成式已经完全不同了。它存储的是一个

    2024年02月05日
    浏览(7)
  • 5.6 Mybatis代码生成器Mybatis Generator (MBG)实战详解

    本文我们主要实战Mybatis官方的代码生成器:Mybatis Generator(MBG),掌握它以后,可以简化大部分手写代码,我们只需要写复杂逻辑代码! 通过前几篇,我们掌握了在SpringBoot下Mybatis的基本用法,操作步骤回顾一下: 创建与MySQL表对应的Java PO对象,字段一一对应; 创建Mapper接口,

    2024年02月05日
    浏览(5)
  • mybatis-plus-generator-ui 可视化代码生成器!

    它提供交互式的Web UI用于生成兼容mybatis-plus框架的相关功能代码,包括Entity,Mapper,Mapper.xml,Service,Controller等。 可以自定义模板以及各类输出参数,也可通过SQL查询语句直接生成代码。 git地址 :https://github.com/davidfantasy/mybatis-plus-generator-ui 1、引入依赖 2、新建程序入口,以main函

    2024年02月08日
    浏览(25)
  • springboot的代码生成器mybatis-plus-generator-ui

    GeberatorUIServer 在springboot的test中运行 这段代码是一个用于生成 MyBatis-Plus 代码的工具类,进行解释: 这是一个名为 GeberatorUIServer 的类。 这是程序的入口方法,即 main 方法。 这段代码创建了一个 GeneratorConfig 对象,并使用链式调用的方式设置了一系列参数: jdbcUrl :数据库连

    2024年02月10日
    浏览(10)
  • 【Python 4】列表与元组slice切片 迭代 列表生成式 生成器generator 迭代器Iterator对象

    在Python中,代码不是越多越好,而是越少越好 取一个list或tuple的部分元素是非常常见的操作 对这种经常取指定索引范围的操作,用循环十分繁琐,因此,Python提供了切片(Slice)操作符,能大大简化这种操作 L[0:3]表示,从索引0开始取,直到索引3为止,但不包括索引3 如果第

    2024年02月07日
    浏览(12)
  • 【沐风老师】3dMax道路标识生成器插件Road Markings Generator使用方法详解

            Road Markings Generator道路标记生成器是一个高效的脚本,可以在3dsMax中自动创建道路标记的2D遮罩。它可以灵活地应用于Archviz项目,同时为3D艺术家节省大量时间。           【主要特点】 -创建可编辑的自定义人行横道线。 -可编辑街道侧面和中间的虚线和连续线。

    2023年04月23日
    浏览(10)
  • Java:mybatis-plus-generator-ui 基于Mybatis-Plus的代码自助生成器

    引用官方文档上的简介: 提供交互式的Web UI用于生成兼容mybatis-plus框架的相关功能代码,包括Entity,Mapper,Mapper.xml,Service,Controller等 ,可以自定义模板以及各类输出参数,也可通过SQL查询语句直接生成代码。 文档 github: https://github.com/davidfantasy/mybatis-plus-generator-ui gitee: https://g

    2024年02月10日
    浏览(9)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包