Skip to content
On this page

迭代器和生成器


WARNING

大量 code 警告

迭代器

背景知识

  1. 什么是迭代?

从一个数据集合中按照一定的顺序,不断取出数据的过程

  1. 迭代和遍历的区别?

迭代强调的是依次取数据,并不保证取多少,也不保证把所有的数据取完 遍历强调的是要把整个数据依次全部取出

  1. 迭代器

对迭代过程的封装,在不同的语言中有不同的表现形式,通常为对象

  1. 迭代模式

一种设计模式,用于统一迭代过程,并规范了迭代器规格:

  • 迭代器应该具有得到下一个数据的能力
  • 迭代器应该具有判断是否还有后续数据的能力

JS中的迭代器

JS规定,如果一个对象具有next方法,并且该方法返回一个对象,该对象的格式如下:

javascript
{value: , done: 是否迭代完成}
{value:, done: 是否迭代完成}

模板

javascript
const obj = {
  next() {
    return {
      value:xxx,
      done:xx
    }
  }
}
const obj = {
  next() {
    return {
      value:xxx,
      done:xx
    }
  }
}

则认为该对象obj是一个迭代器 含义:

  • next方法:用于得到下一个数据
  • 返回的对象
    • value:下一个数据的值
    • done:boolean,是否迭代完成

演示1

javascript
const arr = [1, 2, 3, 4, 5];
//迭代数组arr
const iterator = {
  i: 0, //当前的数组下标
  next() {
    var result = {
      value: arr[this.i],
      done: this.i >= arr.length//下标越界了
    }
    this.i++;
    return result;
  }
}
//让迭代器不断的取出下一个数据,直到没有数据为止
let data = iterator.next();
while (!data.done) { //只要没有迭代完成,则取出数据
  console.log(data.value)
  //进行下一次迭代
  data = iterator.next();
}

console.log("迭代完成")
const arr = [1, 2, 3, 4, 5];
//迭代数组arr
const iterator = {
  i: 0, //当前的数组下标
  next() {
    var result = {
      value: arr[this.i],
      done: this.i >= arr.length//下标越界了
    }
    this.i++;
    return result;
  }
}
//让迭代器不断的取出下一个数据,直到没有数据为止
let data = iterator.next();
while (!data.done) { //只要没有迭代完成,则取出数据
  console.log(data.value)
  //进行下一次迭代
  data = iterator.next();
}

console.log("迭代完成")

演示2

javascript
const arr1 = [1, 2, 3, 4, 5];
const arr2 = [6, 7, 8, 9];
// 迭代器创建函数  iterator creator
function createIterator(arr) {
  let i = 0;//当前的数组下标
  return {
    next() {
      var result = {
        value: arr[i],
        done: i >= arr.length
      }
      i++;
      return result;
    }
  }
}
const iter1 = createIterator(arr1);
iter1.next()
const iter2 = createIterator(arr2);
iter2.next()
const arr1 = [1, 2, 3, 4, 5];
const arr2 = [6, 7, 8, 9];
// 迭代器创建函数  iterator creator
function createIterator(arr) {
  let i = 0;//当前的数组下标
  return {
    next() {
      var result = {
        value: arr[i],
        done: i >= arr.length
      }
      i++;
      return result;
    }
  }
}
const iter1 = createIterator(arr1);
iter1.next()
const iter2 = createIterator(arr2);
iter2.next()

演示3:无限延伸功能

javascript
// 依次得到斐波拉契数列前面n位的值
// 1 1 2 3 5 8 13 .....
//创建一个斐波拉契数列的迭代器
function createFeiboIterator() {
  let prev1 = 1,
      prev2 = 1, //当前位置的前1位和前2位
      n = 1; //当前是第几位
  return {
    next() {
      let value;
      if (n <= 2) {
        value = 1;
      } else {
        value = prev1 + prev2;
      }
      const result = {
        value,
        done: false//这个数列无限延伸
      };
      prev2 = prev1;
      prev1 = result.value;
      n++;
      return result;
    }
  }
}
const iterator = createFeiboIterator();
iterator.next()
// 依次得到斐波拉契数列前面n位的值
// 1 1 2 3 5 8 13 .....
//创建一个斐波拉契数列的迭代器
function createFeiboIterator() {
  let prev1 = 1,
      prev2 = 1, //当前位置的前1位和前2位
      n = 1; //当前是第几位
  return {
    next() {
      let value;
      if (n <= 2) {
        value = 1;
      } else {
        value = prev1 + prev2;
      }
      const result = {
        value,
        done: false//这个数列无限延伸
      };
      prev2 = prev1;
      prev1 = result.value;
      n++;
      return result;
    }
  }
}
const iterator = createFeiboIterator();
iterator.next()

可迭代协议与for-of循环

可迭代协议

  • 迭代器(iterator):一个具有next方法的对象,next方法返回下一个数据并且能指示是否迭代完成
  • 迭代器创建函数(iterator creator):一个返回迭代器的函数

可迭代协议

ES6规定,如果一个对象具有知名符号属性Symbol.iterator,并且属性值是一个迭代器创建函数,则该对象是可迭代的(iterable) 数组就是个可迭代对象

思考:如何知晓一个对象是否是可迭代的?答案:数组、类数组、自己写的对象都可以做成可迭代对象

思考:如何遍历一个可迭代对象?答案:for-of循环

for-of 循环

for-of 循环用于遍历可迭代对象,格式如下

javascript
//迭代完成后循环结束
for(const item in iterable){
    //iterable:可迭代对象
    //item:每次迭代得到的数据
}
//迭代完成后循环结束
for(const item in iterable){
    //iterable:可迭代对象
    //item:每次迭代得到的数据
}

for-of循环的原理

调用对象的[Symbol.iterator]方法,得到一个迭代器。不断调用next方法,只有返回的done为false,则将返回的value传递给变量,然后进入循环体执行一次。

演示:数组就是个可迭代对象

javascript
const arr = [5, 7, 2, 3, 6];
// const iterator = arr[Symbol.iterator]();
// let result = iterator.next();
// while (!result.done) {
//     const item = result.value; //取出数据
//     console.log(item);
//     //下一次迭代
//     result = iterator.next();
// }
// 等价于
for (const item of arr) {
  console.log(item)
}
const arr = [5, 7, 2, 3, 6];
// const iterator = arr[Symbol.iterator]();
// let result = iterator.next();
// while (!result.done) {
//     const item = result.value; //取出数据
//     console.log(item);
//     //下一次迭代
//     result = iterator.next();
// }
// 等价于
for (const item of arr) {
  console.log(item)
}

演示3

html
<div>1</div>
<div>2</div>
<div>3</div>
<script>
  const divs = document.querySelectorAll("div");
  // const iterator = divs[Symbol.iterator]()
  // let result = iterator.next();
  // while (!result.done) {
  //     const item = result.value; //取出数据
  //     console.log(item);
  //     //下一次迭代
  //     result = iterator.next();
  // }
  // 等价于
  for (const item of divs) {
    console.log(item);
  }
</script>
<div>1</div>
<div>2</div>
<div>3</div>
<script>
  const divs = document.querySelectorAll("div");
  // const iterator = divs[Symbol.iterator]()
  // let result = iterator.next();
  // while (!result.done) {
  //     const item = result.value; //取出数据
  //     console.log(item);
  //     //下一次迭代
  //     result = iterator.next();
  // }
  // 等价于
  for (const item of divs) {
    console.log(item);
  }
</script>

演示:自定义可迭代对象

javascript
//可迭代对象
var obj = {
  a: 1,
  b: 2,
  [Symbol.iterator]() {
    const keys = Object.keys(this);//得到对象this里面所有属性名的一个数组
    // console.log(keys)
    let i = 0;
    return {
      next: () => {
        const propName = keys[i];
        const propValue = this[propName];
        const result = {
          value: {
            propName,
            propValue
          },
          done: i >= keys.length
        }
        i++;
        return result;
      }
    }
  }
} 
for (const item of obj) {
  console.log(item); // {propName:"a", propValue:1}
}
//可迭代对象
var obj = {
  a: 1,
  b: 2,
  [Symbol.iterator]() {
    const keys = Object.keys(this);//得到对象this里面所有属性名的一个数组
    // console.log(keys)
    let i = 0;
    return {
      next: () => {
        const propName = keys[i];
        const propValue = this[propName];
        const result = {
          value: {
            propName,
            propValue
          },
          done: i >= keys.length
        }
        i++;
        return result;
      }
    }
  }
} 
for (const item of obj) {
  console.log(item); // {propName:"a", propValue:1}
}

展开运算符与可迭代对象

展开运算符可以作用于可迭代对象,这样,就可以轻松的将可迭代对象转换为数组。 演示

javascript
var obj = {
  a: 1,
  b: 2,
  [Symbol.iterator]() {
    const keys = Object.keys(this);
    let i = 0;
    return {
      next: () => {
        const propName = keys[i];
        const propValue = this[propName];
        const result = {
          value: {
            propName,
            propValue
          },
          done: i >= keys.length
        }
        i++;
        return result;
      }
    }
  }
}

const arr = [...obj];
console.log(arr);

function test(a, b) {
  console.log(a, b)
}

test(...obj);
var obj = {
  a: 1,
  b: 2,
  [Symbol.iterator]() {
    const keys = Object.keys(this);
    let i = 0;
    return {
      next: () => {
        const propName = keys[i];
        const propValue = this[propName];
        const result = {
          value: {
            propName,
            propValue
          },
          done: i >= keys.length
        }
        i++;
        return result;
      }
    }
  }
}

const arr = [...obj];
console.log(arr);

function test(a, b) {
  console.log(a, b)
}

test(...obj);

迭代器和可迭代协议,解决实际问题

解决副作用的 redux 中间件

  • redux-thunk:需要改动action,可接收action是一个函数

  • redux-promise:需要改动action,可接收action是一个promise对象,或action的payload是一个promise对象

以上两个中间件,会导致action或action创建函数不再纯净。

  • redux-saga将解决这样的问题,它不仅可以保持action、action创建函数、reducer的纯净,而且可以用模块化的方式解决副作用,并且功能非常强大。

redux-saga是建立在ES6的生成器基础上的,要熟练的使用saga,必须理解生成器。

要理解生成器,必须先理解迭代器和可迭代协议。

生成器

生成器 (Generator)

  1. 什么是生成器?

生成器是一个通过构造函数Generator创建的对象,生成器既是一个迭代器(有next方法),同时又是一个可迭代对象(有知名符号),可以用于for of循环

javascript
//伪代码

var generator = new Generator();
generator.next();//它具有next方法
var iterator = generator[Symbol.iterator];//它也是一个可迭代对象
for(const item of generator){
    //由于它是一个可迭代对象,因此也可以使用for of循环
}
//伪代码

var generator = new Generator();
generator.next();//它具有next方法
var iterator = generator[Symbol.iterator];//它也是一个可迭代对象
for(const item of generator){
    //由于它是一个可迭代对象,因此也可以使用for of循环
}

注意:Generator构造函数,不提供给开发者使用,仅作为JS引擎内部使用

  1. 如何创建生成器

生成器的创建,必须使用生成器函数(Generator Function)

  1. 如何书写一个生成器函数呢?
javascript
//这是一个生成器函数,该函数一定返回一个生成器
function* method(){
}
//这是一个生成器函数,该函数一定返回一个生成器
function* method(){
}
  1. 生成器函数内部是如何执行的?

第一次调用只是生成了生成器,并没有调用里面 生成器函数内部是为了给生成器的每次迭代提供的数据 每次调用生成器的next方法,将导致生成器函数运行到下一个yield关键字位置 yield是一个关键字,该关键字只能在生成器函数内部使用,表达“产生”一个迭代数据。

  1. 有哪些需要注意的细节?

1). 生成器函数可以有返回值,返回值出现在第一次done为true时的value属性中 2). 调用生成器的next方法时,可以传递参数,传递的参数会交给yield表达式的返回值 3). 第一次调用next方法时,传参没有任何意义 4). 在生成器函数内部,可以调用其他生成器函数,但是要注意加上*号

  1. 生成器的其他API
  • return方法:调用该方法,可以提前结束生成器函数,从而提前让整个迭代过程结束
javascript
generator.return()//结束了,done为true
generator.return(3)//value=3
generator.return()//结束了,done为true
generator.return(3)//value=3
  • throw方法:调用该方法,可以在生成器中产生一个错误
javascript
generator.throw(new Error("sdbjjas"))	
generator.throw(new Error("sdbjjas"))	

演示

javascript
function* test() {
  console.log("第1次运行")
  yield 1;
  console.log("第2次运行")
  yield 2;
  console.log("第3次运行")
}

const generator = test();
function* test() {
  console.log("第1次运行")
  yield 1;
  console.log("第2次运行")
  yield 2;
  console.log("第3次运行")
}

const generator = test();

优化演示:数组的迭代

javascript
const arr1 = [1, 2, 3, 4, 5];
const arr2 = [6, 7, 8, 9];

// 迭代器创建函数  iterator creator
function* createIterator(arr) {
  for (const item of arr) {
    yield item;
  }
}

const iter1 = createIterator(arr1);
const iter2 = createIterator(arr2);
const arr1 = [1, 2, 3, 4, 5];
const arr2 = [6, 7, 8, 9];

// 迭代器创建函数  iterator creator
function* createIterator(arr) {
  for (const item of arr) {
    yield item;
  }
}

const iter1 = createIterator(arr1);
const iter2 = createIterator(arr2);

创建一个斐波拉契数列的迭代器

javascript
function* createFeiboIterator() {
  let prev1 = 1,
      prev2 = 1, //当前位置的前1位和前2位
      n = 1; //当前是第几位
  while (true) {
    if (n <= 2) {
      yield 1;
    } else {
      const newValue = prev1 + prev2
      yield newValue;
      prev2 = prev1;
      prev1 = newValue;
    }
    n++;
  }
}

const iterator = createFeiboIterator();
function* createFeiboIterator() {
  let prev1 = 1,
      prev2 = 1, //当前位置的前1位和前2位
      n = 1; //当前是第几位
  while (true) {
    if (n <= 2) {
      yield 1;
    } else {
      const newValue = prev1 + prev2
      yield newValue;
      prev2 = prev1;
      prev1 = newValue;
    }
    n++;
  }
}

const iterator = createFeiboIterator();

演示:生成器函数可以有返回值,返回值出现在第一次done为true时的value属性中

javascript
function* test() {
  console.log("第1次运行")
  yield 1;
  console.log("第2次运行")
  yield 2;
  console.log("第3次运行");
  return 10;
}

const generator = test();
function* test() {
  console.log("第1次运行")
  yield 1;
  console.log("第2次运行")
  yield 2;
  console.log("第3次运行");
  return 10;
}

const generator = test();

演示:调用生成器的next方法时,可以传递参数,传递的参数会交给yield表达式的返回值

javascript
function* test() {
  console.log("函数开始")
  let info = yield 1;//这里info不是1,这里根据参数动态变化
  console.log(info)
  info = yield 2 + info;
  console.log(info)
}
const generator = test();
控制台
generator.next()
demo.js:2 函数开始
{value: 1, done: false}
generator.next(5)
{value: 7, done: false}
function* test() {
  console.log("函数开始")
  let info = yield 1;//这里info不是1,这里根据参数动态变化
  console.log(info)
  info = yield 2 + info;
  console.log(info)
}
const generator = test();
控制台
generator.next()
demo.js:2 函数开始
{value: 1, done: false}
generator.next(5)
{value: 7, done: false}

演示:在生成器函数内部,可以调用其他生成器函数,但是要注意加上*号

javascript
function* t1(){
  yield "a"
  yield "b"
}
function* test() {
  两种错误的调用方式
  1. t1()// 调用生成器函数不会导致里面的代码执行
  2. yield t1();// 将生成器对象给了yield执行
  yield* t1();//相当于把t1里面代码直接copy过来了
  yield 1;
  yield 2;
  yield 3;
}
const generator = test();
function* t1(){
  yield "a"
  yield "b"
}
function* test() {
  两种错误的调用方式:
  1. t1()// 调用生成器函数不会导致里面的代码执行
  2. yield t1();// 将生成器对象给了yield执行
  yield* t1();//相当于把t1里面代码直接copy过来了
  yield 1;
  yield 2;
  yield 3;
}
const generator = test();

生成器

javascript
// generator.next === generator[Symbol.iterator]().next()
//下面的函数是一个生成器函数,用于创建生成器
function* createGenerator() {
  console.log("生成器函数的函数体 - 开始");
  yield 1; // {value:1, done:false}
  console.log("生成器函数的函数体 - 运行1");
  yield 2;
  console.log("生成器函数的函数体 - 运行2");
  yield 3;
  console.log("生成器函数的函数体 - 运行3");
  return "结束";
}

var generator = createGenerator(); //调用后,一定得到一个生成器
// generator.next === generator[Symbol.iterator]().next()
//下面的函数是一个生成器函数,用于创建生成器
function* createGenerator() {
  console.log("生成器函数的函数体 - 开始");
  yield 1; // {value:1, done:false}
  console.log("生成器函数的函数体 - 运行1");
  yield 2;
  console.log("生成器函数的函数体 - 运行2");
  yield 3;
  console.log("生成器函数的函数体 - 运行3");
  return "结束";
}

var generator = createGenerator(); //调用后,一定得到一个生成器
javascript
function* createArrayIterator(arr) {
  for (let i = 0; i < arr.length; i++) {
    const item = arr[i];
    console.log(`第${i}次迭代`);
    yield item;
  }
  console.log("函数结束");//迭代完成后运行
}

var generator = createArrayIterator([1, 2, 3, 4, 5, 6]);
function* createArrayIterator(arr) {
  for (let i = 0; i < arr.length; i++) {
    const item = arr[i];
    console.log(`第${i}次迭代`);
    yield item;
  }
  console.log("函数结束");//迭代完成后运行
}

var generator = createArrayIterator([1, 2, 3, 4, 5, 6]);
javascript
//下面的函数是一个生成器函数,用于创建生成器
function* createGenerator() {
  console.log("生成器函数的函数体 - 开始");
  yield 1; // {value:1, done:false}
  return;// 后面不会运行了
  console.log("生成器函数的函数体 - 运行1");
  yield 2;
  console.log("生成器函数的函数体 - 运行2");
  yield 3;
  console.log("生成器函数的函数体 - 运行3");
  return "结束";
}

var generator = createGenerator(); //调用后,一定得到一个生成器
//下面的函数是一个生成器函数,用于创建生成器
function* createGenerator() {
  console.log("生成器函数的函数体 - 开始");
  yield 1; // {value:1, done:false}
  return;// 后面不会运行了
  console.log("生成器函数的函数体 - 运行1");
  yield 2;
  console.log("生成器函数的函数体 - 运行2");
  yield 3;
  console.log("生成器函数的函数体 - 运行3");
  return "结束";
}

var generator = createGenerator(); //调用后,一定得到一个生成器
javascript
function asyncGetData() {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve("成哥")
    }, 2000);
  })
}

function* task() {
  console.log("开始获取数据....");
  const data = yield asyncGetData()
  console.log("获取到数据:", data);
  const data2 = yield asyncGetData();
  console.log("又获取到了数据:", data2);
  const data3 = yield 1;
  console.log("又获取到了数据:", data3)
}

/**
         * 通用函数:运行一个生成器任务
         */
function run(generatorFunction) {
  const generator = generatorFunction(); //得到一个生成器
  next();

  /**
             * 封装了generator的next方法,进行下一次迭代
             */
  function next(nextValue) {
    const result = generator.next(nextValue);
    if (result.done) {
      //迭代结束了
      return; 
    }
    const value = result.value; //拿到迭代的数据
    if (typeof value.then === "function") {
      //迭代的数据是一个Promise
      value.then(data => next(data));
    } else {
      next(result.value);
    }
  }
}

run(task);
function asyncGetData() {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve("成哥")
    }, 2000);
  })
}

function* task() {
  console.log("开始获取数据....");
  const data = yield asyncGetData()
  console.log("获取到数据:", data);
  const data2 = yield asyncGetData();
  console.log("又获取到了数据:", data2);
  const data3 = yield 1;
  console.log("又获取到了数据:", data3)
}

/**
         * 通用函数:运行一个生成器任务
         */
function run(generatorFunction) {
  const generator = generatorFunction(); //得到一个生成器
  next();

  /**
             * 封装了generator的next方法,进行下一次迭代
             */
  function next(nextValue) {
    const result = generator.next(nextValue);
    if (result.done) {
      //迭代结束了
      return; 
    }
    const value = result.value; //拿到迭代的数据
    if (typeof value.then === "function") {
      //迭代的数据是一个Promise
      value.then(data => next(data));
    } else {
      next(result.value);
    }
  }
}

run(task);
javascript
//下面的函数是一个生成器函数,用于创建生成器
function* createGenerator() {
  console.log("生成器函数的函数体 - 开始");
  let result = yield 1; //将1作为第一次的迭代的值,还没有完成赋值 绝不是把1赋值给result,result是外部给他的
  console.log("生成器函数的函数体 - 运行1", result);
  result = yield 2; //将2作为第二次迭代的值
  console.log("生成器函数的函数体 - 运行2", result);
  result = yield 3;
  console.log("生成器函数的函数体 - 运行3", result);
  return "结束";
}

var generator = createGenerator(); //调用后,一定得到一个生成器

var result = generator.next(); //{value:1, done:false}
while (!result.done) {
  //有迭代的值
  result = generator.next(result.value);//如果想把yield返回的值交给result
}
//下面的函数是一个生成器函数,用于创建生成器
function* createGenerator() {
  console.log("生成器函数的函数体 - 开始");
  let result = yield 1; //将1作为第一次的迭代的值,还没有完成赋值 绝不是把1赋值给result,result是外部给他的
  console.log("生成器函数的函数体 - 运行1", result);
  result = yield 2; //将2作为第二次迭代的值
  console.log("生成器函数的函数体 - 运行2", result);
  result = yield 3;
  console.log("生成器函数的函数体 - 运行3", result);
  return "结束";
}

var generator = createGenerator(); //调用后,一定得到一个生成器

var result = generator.next(); //{value:1, done:false}
while (!result.done) {
  //有迭代的值
  result = generator.next(result.value);//如果想把yield返回的值交给result
}
javascript
//下面的函数是一个生成器函数,用于创建生成器
function* createGenerator() {
  try {
    console.log("生成器函数的函数体 - 开始");
    let result = yield 1; //将1作为第一次的迭代的值.
    console.log("生成器函数的函数体 - 运行1", result);
    result = yield 2; //将2作为第二次迭代的值
    console.log("生成器函数的函数体 - 运行2", result);
    result = yield 3;
    console.log("生成器函数的函数体 - 运行3", result);
    return "结束";
  } catch (err) {
    console.log("报错了");
    yield "Abc";
  }

}

var generator = createGenerator(); //调用后,一定得到一个生成器
//下面的函数是一个生成器函数,用于创建生成器
function* createGenerator() {
  try {
    console.log("生成器函数的函数体 - 开始");
    let result = yield 1; //将1作为第一次的迭代的值.
    console.log("生成器函数的函数体 - 运行1", result);
    result = yield 2; //将2作为第二次迭代的值
    console.log("生成器函数的函数体 - 运行2", result);
    result = yield 3;
    console.log("生成器函数的函数体 - 运行3", result);
    return "结束";
  } catch (err) {
    console.log("报错了");
    yield "Abc";
  }

}

var generator = createGenerator(); //调用后,一定得到一个生成器
javascript
//下面的函数是一个生成器函数,用于创建生成器
function* createGenerator() {
  console.log("生成器函数的函数体 - 开始");
  let result = yield 1; //将1作为第一次的迭代的值
  console.log("生成器函数的函数体 - 运行1", result);
  result = yield 2; //将2作为第二次迭代的值
  console.log("生成器函数的函数体 - 运行2", result);
  result = yield 3;
  console.log("生成器函数的函数体 - 运行3", result);
  return "结束";

}

var generator = createGenerator(); //调用后,一定得到一个生成器
//下面的函数是一个生成器函数,用于创建生成器
function* createGenerator() {
  console.log("生成器函数的函数体 - 开始");
  let result = yield 1; //将1作为第一次的迭代的值
  console.log("生成器函数的函数体 - 运行1", result);
  result = yield 2; //将2作为第二次迭代的值
  console.log("生成器函数的函数体 - 运行2", result);
  result = yield 3;
  console.log("生成器函数的函数体 - 运行3", result);
  return "结束";

}

var generator = createGenerator(); //调用后,一定得到一个生成器
javascript
function* g2() {
  console.log("g2-开始");
  let result = yield "g1";
  console.log("g2-运行1");
  result = yield "g2";
  return 123;
}
//下面的函数是一个生成器函数,用于创建生成器
function* createGenerator() {
  console.log("生成器函数的函数体 - 开始");
  let result = yield 1; //将1作为第一次的迭代的值
  result = yield* g2(); //result为g2函数的返回值
  console.log("生成器函数的函数体 - 运行1", result);
  result = yield 2; //将2作为第二次迭代的值
  console.log("生成器函数的函数体 - 运行2", result);
  result = yield 3;
  console.log("生成器函数的函数体 - 运行3", result);
  return "结束";

}

var generator = createGenerator(); //调用后,一定得到一个生成器

function* g2() {
  console.log("g2-开始");
  let result = yield "g1";
  console.log("g2-运行1");
  result = yield "g2";
  return 123;
}
//下面的函数是一个生成器函数,用于创建生成器
function* createGenerator() {
  console.log("生成器函数的函数体 - 开始");
  let result = yield 1; //将1作为第一次的迭代的值
  result = yield* g2(); //result为g2函数的返回值
  console.log("生成器函数的函数体 - 运行1", result);
  result = yield 2; //将2作为第二次迭代的值
  console.log("生成器函数的函数体 - 运行2", result);
  result = yield 3;
  console.log("生成器函数的函数体 - 运行3", result);
  return "结束";

}

var generator = createGenerator(); //调用后,一定得到一个生成器

生成器函数特点

  1. 调用生成器函数,会返回一个生成器,而不是执行函数体(因为,生成器函数的函数体执行,收到生成器控制)
  2. 每当调用了生成器的next方法,生成器的函数体会从上一次yield的位置(或开始位置)运行到下一个yield
    • yield关键字只能在生成器内部使用,不可以在普通函数内部使用
    • 它表示暂停,并返回一个当前迭代的数据
    • 如果没有下一个yield,到了函数结束,则生成器的next方法得到的结果中的done为true
  3. yield关键字后面的表达式返回的数据,会作为当前迭代的数据
  4. 生成器函数的返回值,会作为迭代结束时的value
    • 但是,如果在结束过后,仍然反复调用next,则value为undefined
  5. 生成器调用next的时候,可以传递参数,该参数会作为生成器函数体上一次yield表达式的值。
    • 生成器第一次调用next函数时,传递参数没有任何意义
  6. 生成器带有一个throw方法,该方法与next的效果相同,唯一的区别在于:
    • next方法传递的参数会被返回成一个正常值
    • throw方法传递的参数是一个错误对象,会导致生成器函数内部发生一个错误。
  7. 生成器带有一个return方法,该方法会直接结束生成器函数
  8. 若需要在生成器内部调用其他生成器,注意:如果直接调用,得到的是一个生成器,如果加入*号调用,则进入其生成器内部执行。如果是yield* 函数()调用生成器函数,则该函数的返回结果,为该表达式的结果
javascript
//下面的函数是一个生成器函数,用于创建生成器
function* createGenerator() {
  console.log("生成器函数的函数体 - 开始");
  yield 1; // {value:1, done:false}
  console.log("生成器函数的函数体 - 运行1");
  yield 2;
  console.log("生成器函数的函数体 - 运行2");
  yield 3;
  console.log("生成器函数的函数体 - 运行3");
  return "结束";
}

var generator = createGenerator(); //调用后,一定得到一个生成器
//下面的函数是一个生成器函数,用于创建生成器
function* createGenerator() {
  console.log("生成器函数的函数体 - 开始");
  yield 1; // {value:1, done:false}
  console.log("生成器函数的函数体 - 运行1");
  yield 2;
  console.log("生成器函数的函数体 - 运行2");
  yield 3;
  console.log("生成器函数的函数体 - 运行3");
  return "结束";
}

var generator = createGenerator(); //调用后,一定得到一个生成器
javascript
function* createArrayIterator(arr) {
  for (let i = 0; i < arr.length; i++) {
    const item = arr[i];
    console.log(`第${i}次迭代`)
    yield item;
  }
  console.log("函数结束")
}

var generator = createArrayIterator([1, 2, 3, 4, 5, 6])
function* createArrayIterator(arr) {
  for (let i = 0; i < arr.length; i++) {
    const item = arr[i];
    console.log(`第${i}次迭代`)
    yield item;
  }
  console.log("函数结束")
}

var generator = createArrayIterator([1, 2, 3, 4, 5, 6])
javascript
//下面的函数是一个生成器函数,用于创建生成器
function* createGenerator() {
  console.log("生成器函数的函数体 - 开始");
  yield 1; // {value:1, done:false}
  return;
  console.log("生成器函数的函数体 - 运行1");
  yield 2;
  console.log("生成器函数的函数体 - 运行2");
  yield 3;
  console.log("生成器函数的函数体 - 运行3");
  return "结束";
}

var generator = createGenerator(); //调用后,一定得到一个生成器

//下面的函数是一个生成器函数,用于创建生成器
function* createGenerator() {
  console.log("生成器函数的函数体 - 开始");
  yield 1; // {value:1, done:false}
  return;
  console.log("生成器函数的函数体 - 运行1");
  yield 2;
  console.log("生成器函数的函数体 - 运行2");
  yield 3;
  console.log("生成器函数的函数体 - 运行3");
  return "结束";
}

var generator = createGenerator(); //调用后,一定得到一个生成器

javascript
//下面的函数是一个生成器函数,用于创建生成器
function* createGenerator() {
  console.log("生成器函数的函数体 - 开始");
  let result = yield 1; //将1作为第一次的迭代的值
  console.log("生成器函数的函数体 - 运行1", result);
  result = yield 2; //将2作为第二次迭代的值
  console.log("生成器函数的函数体 - 运行2", result);
  result = yield 3;
  console.log("生成器函数的函数体 - 运行3", result);
  return "结束";
}

var generator = createGenerator(); //调用后,一定得到一个生成器

var result = generator.next(); //{value:1, done:false}
while (!result.done) {
  //有迭代的值
  result = generator.next(result.value);
}
//下面的函数是一个生成器函数,用于创建生成器
function* createGenerator() {
  console.log("生成器函数的函数体 - 开始");
  let result = yield 1; //将1作为第一次的迭代的值
  console.log("生成器函数的函数体 - 运行1", result);
  result = yield 2; //将2作为第二次迭代的值
  console.log("生成器函数的函数体 - 运行2", result);
  result = yield 3;
  console.log("生成器函数的函数体 - 运行3", result);
  return "结束";
}

var generator = createGenerator(); //调用后,一定得到一个生成器

var result = generator.next(); //{value:1, done:false}
while (!result.done) {
  //有迭代的值
  result = generator.next(result.value);
}
javascript
function asyncGetData() {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve("成哥")
    }, 2000);
  })
}

function* task() {
  console.log("开始获取数据....");
  const data = yield asyncGetData()
  console.log("获取到数据:", data);
  const data2 = yield asyncGetData();
  console.log("又获取到了数据:", data2);
  const data3 = yield 1;
  console.log("又获取到了数据:", data3)
}

/**
         * 通用函数:运行一个生成器任务
         */
function run(generatorFunction) {
  const generator = generatorFunction(); //得到一个生成器
  next();

  /**
             * 封装了generator的next方法,进行下一次迭代
             */
  function next(nextValue) {
    const result = generator.next(nextValue);
    if (result.done) {
      //迭代结束了
      return; 
    }
    const value = result.value; //拿到迭代的数据
    if (typeof value.then === "function") {
      //迭代的数据是一个Promise
      value.then(data => next(data));
    } else {
      next(result.value);
    }
  }
}

run(task);
function asyncGetData() {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve("成哥")
    }, 2000);
  })
}

function* task() {
  console.log("开始获取数据....");
  const data = yield asyncGetData()
  console.log("获取到数据:", data);
  const data2 = yield asyncGetData();
  console.log("又获取到了数据:", data2);
  const data3 = yield 1;
  console.log("又获取到了数据:", data3)
}

/**
         * 通用函数:运行一个生成器任务
         */
function run(generatorFunction) {
  const generator = generatorFunction(); //得到一个生成器
  next();

  /**
             * 封装了generator的next方法,进行下一次迭代
             */
  function next(nextValue) {
    const result = generator.next(nextValue);
    if (result.done) {
      //迭代结束了
      return; 
    }
    const value = result.value; //拿到迭代的数据
    if (typeof value.then === "function") {
      //迭代的数据是一个Promise
      value.then(data => next(data));
    } else {
      next(result.value);
    }
  }
}

run(task);
javascript
//下面的函数是一个生成器函数,用于创建生成器
function* createGenerator() {
  try {
    console.log("生成器函数的函数体 - 开始");
    let result = yield 1; //将1作为第一次的迭代的值
    console.log("生成器函数的函数体 - 运行1", result);
    result = yield 2; //将2作为第二次迭代的值
    console.log("生成器函数的函数体 - 运行2", result);
    result = yield 3;
    console.log("生成器函数的函数体 - 运行3", result);
    return "结束";
  } catch (err) {
    console.log("报错了");
    yield "Abc";
  }
}

var generator = createGenerator(); //调用后,一定得到一个生成器
//下面的函数是一个生成器函数,用于创建生成器
function* createGenerator() {
  try {
    console.log("生成器函数的函数体 - 开始");
    let result = yield 1; //将1作为第一次的迭代的值
    console.log("生成器函数的函数体 - 运行1", result);
    result = yield 2; //将2作为第二次迭代的值
    console.log("生成器函数的函数体 - 运行2", result);
    result = yield 3;
    console.log("生成器函数的函数体 - 运行3", result);
    return "结束";
  } catch (err) {
    console.log("报错了");
    yield "Abc";
  }
}

var generator = createGenerator(); //调用后,一定得到一个生成器
javascript
//下面的函数是一个生成器函数,用于创建生成器
function* createGenerator() {
  console.log("生成器函数的函数体 - 开始");
  let result = yield 1; //将1作为第一次的迭代的值
  console.log("生成器函数的函数体 - 运行1", result);
  result = yield 2; //将2作为第二次迭代的值
  console.log("生成器函数的函数体 - 运行2", result);
  result = yield 3;
  console.log("生成器函数的函数体 - 运行3", result);
  return "结束";
}

var generator = createGenerator(); //调用后,一定得到一个生成器
//下面的函数是一个生成器函数,用于创建生成器
function* createGenerator() {
  console.log("生成器函数的函数体 - 开始");
  let result = yield 1; //将1作为第一次的迭代的值
  console.log("生成器函数的函数体 - 运行1", result);
  result = yield 2; //将2作为第二次迭代的值
  console.log("生成器函数的函数体 - 运行2", result);
  result = yield 3;
  console.log("生成器函数的函数体 - 运行3", result);
  return "结束";
}

var generator = createGenerator(); //调用后,一定得到一个生成器
javascript
function* g2() {
  console.log("g2-开始");
  let result = yield "g1";
  console.log("g2-运行1");
  result = yield "g2";
  return 123;
}
//下面的函数是一个生成器函数,用于创建生成器
function* createGenerator() {
  console.log("生成器函数的函数体 - 开始");
  let result = yield 1; //将1作为第一次的迭代的值
  result = yield* g2(); //result为g2函数的返回值
  console.log("生成器函数的函数体 - 运行1", result);
  result = yield 2; //将2作为第二次迭代的值
  console.log("生成器函数的函数体 - 运行2", result);
  result = yield 3;
  console.log("生成器函数的函数体 - 运行3", result);
  return "结束";

}

var generator = createGenerator(); //调用后,一定得到一个生成器
function* g2() {
  console.log("g2-开始");
  let result = yield "g1";
  console.log("g2-运行1");
  result = yield "g2";
  return 123;
}
//下面的函数是一个生成器函数,用于创建生成器
function* createGenerator() {
  console.log("生成器函数的函数体 - 开始");
  let result = yield 1; //将1作为第一次的迭代的值
  result = yield* g2(); //result为g2函数的返回值
  console.log("生成器函数的函数体 - 运行1", result);
  result = yield 2; //将2作为第二次迭代的值
  console.log("生成器函数的函数体 - 运行2", result);
  result = yield 3;
  console.log("生成器函数的函数体 - 运行3", result);
  return "结束";

}

var generator = createGenerator(); //调用后,一定得到一个生成器

生成器应用-异步任务控制

高仿await

javascript
function* task() {
  const d = yield 1;
  console.log(d)
  // //d : 1
  const resp = yield fetch("http://101.132.72.36:5100/api/local")
  const result = yield resp.json();
  console.log(result);
}
run(task)
function run(generatorFunc) {
  const generator = generatorFunc();
  let result = generator.next(); //启动任务(开始迭代), 得到迭代数据
  handleResult();
  //对result进行处理
  function handleResult() {
    if (result.done) {
      return; //迭代完成,不处理
    }
    //迭代没有完成,分为两种情况
    //1. 迭代的数据是一个Promise
    //2. 迭代的数据是其他数据
    if (typeof result.value.then === "function") {//promise里面有个then方法
      //1. 迭代的数据是一个Promise
      //等待Promise完成后,再进行下一次迭代
      result.value.then(data => {
        result = generator.next(data)
        handleResult();
      })
    } else {
      //2. 迭代的数据是其他数据,直接进行下一次迭代
      result = generator.next(result.value)
      handleResult();
    }
  }
}
function* task() {
  const d = yield 1;
  console.log(d)
  // //d : 1
  const resp = yield fetch("http://101.132.72.36:5100/api/local")
  const result = yield resp.json();
  console.log(result);
}
run(task)
function run(generatorFunc) {
  const generator = generatorFunc();
  let result = generator.next(); //启动任务(开始迭代), 得到迭代数据
  handleResult();
  //对result进行处理
  function handleResult() {
    if (result.done) {
      return; //迭代完成,不处理
    }
    //迭代没有完成,分为两种情况
    //1. 迭代的数据是一个Promise
    //2. 迭代的数据是其他数据
    if (typeof result.value.then === "function") {//promise里面有个then方法
      //1. 迭代的数据是一个Promise
      //等待Promise完成后,再进行下一次迭代
      result.value.then(data => {
        result = generator.next(data)
        handleResult();
      })
    } else {
      //2. 迭代的数据是其他数据,直接进行下一次迭代
      result = generator.next(result.value)
      handleResult();
    }
  }
}