最近在写RN相关的东西,其中涉及到了redux-saga ,saga的实现原理就是ES6中的Generator函数,而Generator函数又和迭代器有着密不可分的关系。所以本篇博客先学习总结了iterator相关的东西,然后又介绍了Generator相关的内容,最后介绍了使用Generator进行异步编程。本篇博客所涉及的示例使用TypeScript语言编写,当然所涉及的特性是基于ES6规范的,使用TS语言不影响来阐述和总结ES6的相关特性。下篇博客准备系统梳理一下saga相关的内容。

一、迭代器

之前再聊迭代器模式时,使用Swift语言自定义过迭代器,在TS中也有迭代器。此处的迭代器与之前所介绍的迭代器是大同小异的。首先我们先来自定义一个迭代器,然后再看一下ES6中的迭代器的使用方式。

1、自定义迭代器

下方定义了一个迭代器函数,函数说明如下:

  • 该函数接收一个数组类型的参数,我们可以将需要创建迭代器的数组作为参数传进来。

  • 函数内部定义了一个 nextIndex 参数用来记录迭代器的位置。

  • 该函数返回一个迭代器对象,该迭代器对象包含一个key为 next , value为匿名函数的属性。

  • 这个key为 next 的匿名方法的返回值为每次迭代器的返回结果对象,这个结果对象由 两个属性组成,value表示本次迭代器的值,done表示迭代器遍历是否结束。

  • 遍历到最后,最终返回的值为 { value: undefine, done: true }, 也就意味着迭代器遍历结束,value是undefined, done为true。  

自定义完迭代器后,我们就可以对上述代码进行测试了。

  • 首先创建了一个数组,然后将数组传给 makeIterator 函数。而 makeIterator 函数会返回一个含有next方法的迭代器对象。

  • 我们将这个迭代器对象命名为 iterator,我们就可以通过 iterator的next方法来依次获取数组中的值了。

  • 我们通过 while 循环来不断的调用 iterator中的next方法,直到next方法返回的对象中的done值为true时,表示遍历结束。

  • 遍历结束后,我们再次调用 next() 方法,得到的是{ value: undefind, done: true } 的对象,表示遍历结束,获取的value值为 undefined。

2、ES6中的迭代器

类似于Swift语言的特性,ES6规范中我们可以直接通过一些对象获取该对象所对应的迭代器,如下所示:

  • 下方示例中使用的数组和上面使用的list是一个,首先我们通过 list[Symbol.iterator]() 的方式获取了 list对应的迭代器。(Symbol也是一种数据类型, 该数据类型用来表示独一无二的对象)
  • 该迭代器的使用方式和输出结果与上述我们自定义的迭代器的使用方式完全一致, 输出结果与之前的结果也是一致的。

3、使用 for - of 遍历迭代器

上述方式创建的迭代器我们是使用的while循环来进行遍历的,除了while循环,我们还可以通过for-of 进行遍历。此处的 for - of遍历方式类似于Swift语言中的 for - in循环,可以依次的自动去除迭代器中的值。下方就是使用for - of 来循环遍历创建的迭代器。

从下方示例中我们不难看出直接输出的是迭代器返回对象的value值。

4、在类中添加迭代器

我们可以在自己的类中添加相关方法,使我们自己的类支持迭代器。下方就创建了一个 RangeIterator 类,该类的作用是可以定义一个范围,构造器可以接受两个值,一个是范围的起始位置另一个是范围的结束点。下方我们为该范围类添加了自定义迭代器,具体说明如下:

  • 在该类中添加了一个名为 next 的箭头函数,在该函数中做的事情与之前我们自定义的next方法差不多,主要是用来获取下一个值然后返回。

  • 然后又实现一个[Symbole.iterator]函数,用来获取迭代器对象。

  • 最后我们可看到定义的范围对象可以向迭代器那样使用for-of进行遍历。

5、调用迭代器的场景

迭代器的使用场景还是蛮多的,解构赋值、扩展运算符、Generator函数、yield*, 下方会简单的列举出来。

(1)、对数组或者集合的解构赋值

在下方代码片段中首先创建了一个名为 mySet 的集合对象。然后通过循环给集合中添加了一些值。然后通过 解构赋值 的形式,取出了 mySet 中的第一个值和第二个值。此刻的结构赋值会调用集合的迭代器接口,取出第一个值和第二个值,分别赋值给 first 和 second。

第二个红框中在结构赋值是使用了扩展运算符,该操作符会使 others 接收 firstItem 剩下的值。

(2)、扩展运算符 ...  

接下来来看另一个扩展运算符的例子。

  • 首先定义了一个字符串,然后通过扩展运算符将该字符串的每个字符拆分到一个数组中,输出结果如下所示。
  • 扩展运算符还可以使用到对象上,如第二个示例所示。

(3)、在Generator函数的 yield * 中使用

稍后会详细的介绍 Generator 函数,一个Generator 函数返回的是一个迭代器,我们可以调用该迭代器的 next 方法来执行每一个 yield。在 Generator 函数中,可以使用 yield * 后边跟一个可便遍历的结构,这样我们就可以在外部统一使用 next 来访问这个可遍历的结构的每一个值,如下所示:

二、Generator函数及异步编程

理解完迭代器,接下来来看一下Generator函数。如果做过RN开发的话,如果使用过 redux - saga的话,应该对Generator函数不陌生。Generator函数是ES6提供的异步编程的解决方案,解析了我们先看一下Generator函数基本使用方式,再看一下如何使用Generator函数进行异步编程。

1、Generator函数的定义和使用

下方定义了一个 Generator函数,Generator函数的定义与普通函数的定义差不多,只不过是function关键字后边跟了一个*号。然后函数体内部使用了一个个 yield语句来表明每一步的操作。定义完Generator函数后,下方紧接着的是使用,首先调用该Generator函数获取了一个迭代器,每次执行这个迭代器的next方法都会一次的执行一个yield语句。输出结果和上面的迭代器没啥区别。

2、next的参数

在调用Generator函数返回的迭代器时,是可以往next方法中传入参数的。next 方法可以带一个参数,该参数被当做上一个 yield 语句的返回值。下方就是给 next 传参的一个示例:

  • 下方定义了一个Generator函数,用来输出自增的值,每次调用next都会获取一个自增的值。

  • 当调用 rg.next(true) 时,这个true就会被赋值给 reset, 因为这个reset被视为上个yield的返回值,上一个yield执行后,会将index设置为 -1

  • 那么rg.next(true)对应的 yield执行是,index是从 -1 开始自增的,自增后为0,所以 rg.next(true) 对应的 yield 的值为0。

下方是另一个示例:

  • 下方定义了一个名为testNextValue的Generator函数,该函数本身接收了一个参数。

  • 在调用该Generator函数时,传入了一个参数,这个参数不是Next的参数,是Generator函数本身的参数。Generator函数在调用时,函数体并不会马上执行,在调用next函数时才会执行函数中yield语句体。

  • 第一次调用Next,给Next传入了一个值 5,也就是说明 x = 5。第一次执行next会调用第一个 yield 语句体,test1.next(2) = x + 1 = 5 + 1 = 6, 所以第一次调用next的结果值为 6

  • 第二次调用 Next,传入的Next参数为3。这个3 被作为上一个 yield 语句体的返回值,yield(x + 1) 的返回值为 3。那么 y 的值就为 2 * 3 = 6。yield中的值为 y / 3 = 2所以第二次执行next获取的值为 2

  • 第三次调用Next传入的参数为 4,这个 4 被作为上个yield语句体返回的参数,所以z = 4, 上分析过了 x = 5, y = 6, 所以 x + y + z = 15, 第三次执行next为 15

  • 再次调用Next,因为语句体执行完了,所以获取到的是undefined。

三、使用Generator函数进行异步编程

接下来实现一个简单的示例,使用Generator函数结合Promise回调模拟一下异步编程。

首先定义了一个 getPromise函数,该函数接收两个参数,一个参数表示网络请求的参数,另一个参数表示请求时间。该函数返回一个 Promise对象,在Promise对象中我们使用了setTimeout来模拟请求的延迟,根据传入的timeout来决定延迟时间,延迟时间到达后会执行 resolve方法,将相关值回调出来。

然后定义了一个Generator函数,在该函数中通过yield来调用每个函数,下方的Generator函数比较简单,在此就不做过多赘述了。

然后我们通过for -of 一次执行Generator函数的next方法,进而来执行每个getPromise方法。

  

下方是具体的执行结果,从执行结果中不难看出,每次获取的yield值是一个Promise对象,我们可在该Promise对象的then方法中获取到相关的结果值。从输出顺序中可以看出,会先输出时间小的那个结果。

本篇博客就先到这儿吧,下篇会聊一些saga的相关内容。

ES6中的迭代器、Generator函数以及Generator函数的异步操作的更多相关文章

  1. ES6中的迭代器(Iterator)和生成器(Generator)

    前面的话 用循环语句迭代数据时,必须要初始化一个变量来记录每一次迭代在数据集合中的位置,而在许多编程语言中,已经开始通过程序化的方式用迭代器对象返回迭代过程中集合的每一个元素 迭代器的使用可以极大地简 ...

  2. ES6中的迭代器(Iterator)和生成器(Generator)(一)

    用循环语句迭代数据时,必须要初始化一个变量来记录每一次迭代在数据集合中的位置,而在许多编程语言中,已经开始通过程序化的方式用迭代器对象返回迭代过程中集合的每一个元素 迭代器的使用可以极大地简化数据操作 ...

  3. ES6中的迭代器(Iterator)和生成器(Generator)(二)

    一.内建迭代器 迭代器是ES6的一个重要组成部分,在ES6中,已经默认为许多内建类型提供了内建迭代器,只有当这些内建迭代器无法实现目标时才需要自己创建.通常来说当定义自己的对象和类时才会遇到这种情况, ...

  4. es6中一些基本的使用方法

    es6中一些基本的使用方法 const 定义常量 let 块级变量 用let定义的变量只在块当中起作用,离开变量外界的块(括号)就会被销毁. 模板字面量 用于字符串拼接和写模板,使用 ` (反引号,左 ...

  5. ES5和ES6中关于import & export的书写方式的区别

    ES6中输出变量的写法 情景1:单个变量 输出 export const less = 'less' 引用 import {less} from '../index.js' 情景2:多个变量 输出: ...

  6. ES6中比较实用的几个特性

    1.Default Parameters(默认参数) in ES6 es6之前,定义默认参数的方法是在一个方法内部定义 var link = function (height, color, url) ...

  7. es6 中的generator函数控制流程

    Generator函数跟普通函数的写法有非常大的区别: 一是,function关键字与函数名之间有一个星号: 二是,函数体内部使用yield语句,定义不同的内部状态(yield在英语里的意思就是“产出 ...

  8. ES6中的Generator函数

    今天小编发现一个es6中的新概念,同时也接触到了一个新关键字yeild,下面我就简单和大家聊聊es6中的generator函数.大家还可以关注我的微信公众号,蜗牛全栈. 一.函数声明:在functio ...

  9. ES6的新特性(17)——Generator 函数的异步应用

    Generator 函数的异步应用 异步编程对 JavaScript 语言太重要.Javascript 语言的执行环境是“单线程”的,如果没有异步编程,根本没法用,非卡死不可.本章主要介绍 Gener ...

随机推荐

  1. React中控制台警告

    1.dll_lib.js:1 Warning: bind(): You are binding a component method to the component. React does this ...

  2. Selenium+java - PageFactory设计模式

    前言 上一小节我们已经学习了Page Object设计模式,优势很明显,能更好的体现java的面向对象思想和封装特性.但同时也存在一些不足之处,那就是随着这种模式使用,随着元素定位获取,元素定位与页面 ...

  3. Python 列表深浅复制详解

    在文章<Python 数据类型>里边介绍了列表的用法,其中列表有个 copy() 方法,意思是复制一个相同的列表.例如 names = ["小明", "小红& ...

  4. NLP系列文章:子词嵌入(fastText)的理解!(附代码)

    1. 什么是fastText 英语单词通常有其内部结构和形成⽅式.例如,我们可以从"dog""dogs"和"dogcatcher"的字⾯上推 ...

  5. CentOS7配置网络ip地址

    CentOS7配置网络ip地址 CentOS 7.x版本中没有ifcfg-eth0文件 只有ifcfg-ens33文件(为了符合日常习惯) (1)将文件ifcfg-ens33重命名为ifcfg-eth ...

  6. 良许Linux | Linux学习方法及学习资料汇总

    很多人想学习Linux,却不知道怎么着手,甚至不知道Linux有哪些方向,非常迷茫.基于此,我特地写了篇文章介绍Linux方向性问题,没想到一不小心成了爆款: 到什么程度才叫精通 Linux?​ 看完 ...

  7. 使用Makefile构建Docker

    使用Makefile构建Docker 刚开始学习docker命令的时候,很喜欢一个字一个字敲,因为这样会记住命令.后来熟悉了之后,每次想要做一些操作的时候就不得不 重复的输入以前的命令.当切换一个项目 ...

  8. 学习 Object-C: 简史

    对于一门语言的历史,我认为写一本书可能都不为过,关键是看你如何介绍和表达.当然每一个人的理解也大相径庭.本文阐述也仅仅只是冰山一角,如果需要深入了解,自己可能需要多花费一些心思. 这里也不会给大家说太 ...

  9. unity_小功能实现(敌人巡逻功能)

    利用NavMeshAgent控制敌人巡逻,即敌人在一组位置间循环巡逻. 首先我们要知道NavMeshAgent中有两个方法:1.锁定当前巡逻的某一目标位置,即navMeshAgent.destinat ...

  10. Bootstrap如何禁止响应式布局

    Bootstrap 会自动帮你针对不同的屏幕尺寸调整你的页面,使其在各个尺寸的屏幕上表现良好.下面我们列出了如何禁用这一特性,就像这个非响应式布局实例页面一样. 禁止响应式布局有如下几步: 移除 此 ...