关于ES6 yield的一些想法

自ES6推出以来,yield就一直是其仅次于Promise的第二大难得一个东西,主要难于理解,所以出这篇文章也算是帮助大家理解这一概念。

顺便介绍一些符号和迭代器相关的知识

迭代器

在讲生成器之前,我们先来讲一下迭代器。

迭代器也是ES6推出来的一个新的东西,其可以让开发这控制迭代过程,甚至可以将对象转变为可迭代对象。写为Symbol.iterator,这是ES6推出的知名符号的一种(后面会针对符号讲解一篇文章)

迭代器,顾名思义,就是用来迭代循环的,常见的比如数组、set、map等,我们可以打开控制台查看原型就可以看到:

 这里我们可以私自改写这个原型方法让其变成我们控制的迭代过程,举个例子:

    Array.prototype[Symbol.iterator] = function () {
        let i = 0;
        return {
            next() {
                let data = {
                    value: arr[i],
                    done: arr[i] == undefined,
                };
                console.log(`正在迭代第${i}个元素`);
                i++;
                return data;
            }
        };
    }
    var arr = [1, 2, 3, 4, 5];
    for (const iterator of arr) {

    }

 执行结果如下:

 所以从例子中不难看出,迭代器是一个需要返回一个对象中带有next方法且next方法返回值done和value组成的函数。再比如无限迭代的斐波拉契数列

function createFeiboIterator() {
        let prev1 = 1;
        let prev2 = 1;
        let n = 1;
        return {
            next() {
                if (n <= 2) {
                    value = 1;
                }
                else {
                    value = prev1 + prev2;
                }
                const result = {
                    value: value,
                    done: false,
                };
                prev2 = prev1;
                prev1 = result.value;
                n++;
                return result;
            }
        }
    }
var fib = createFeiboIterator();
console.log(fib.next());
console.log(fib.next());
console.log(fib.next());
console.log(fib.next());
console.log(fib.next());
console.log(fib.next());
console.log(fib.next());
console.log(fib.next());
console.log(fib.next());
console.log(fib.next());
console.log(fib.next());
console.log(fib.next());
console.log(fib.next());
console.log(fib.next());
console.log(fib.next());
console.log(fib.next());
console.log(fib.next());

甚至于你还可以对对象进行自定义迭代器,例如:

const obj = {
  a: 1,
  b: 2,
  [Symbol.iterator]() {
    let entry = Object.entries(this);
    let i = 0;

    return {
      next() {

        let data = {
          done: entry[i] === undefined,
          value: entry[i],
        };

        i++;
        return data;
      }
    }
  }
};

for (const item of obj) {
  console.log(item);
}

执行结果如下:

迭代器缺点

迭代器虽然可以让我们很好的控制迭代过程,但是有一个不小的缺点,太麻烦了,正常人谁会去写控制迭代的代码呢,所以ES6为了减低麻烦,方便开发人员利用好迭代器出来了一个生成器,当然也在迭代器基础上增添了许多其他功能。

 生成器

生成器的语法我就不赘述了,记住生成器函数执行时并不是马上执行,需要调用next方法才能执行即可,所以说生成器函数是一个状态机函数

生成器最开始是用于生成迭代器的,所以一个生成器函数执行一定返回的是个迭代器。

1. 生成器基本使用:改写斐波拉契数列

function* createFeiboIt() {
        let prev1 = 1;
        let prev2 = 1;
        let n = 1;

        while (true) {
            let value = 1;
            if (n > 2) {
                value = prev1 + prev2;
            }
            yield value;
            prev2 = prev1;
            prev1 = value;
            n++;
        }
    }
var fib = createFeiboIt();
console.log(fib.next());
console.log(fib.next());
console.log(fib.next());
console.log(fib.next());
console.log(fib.next());
console.log(fib.next());
console.log(fib.next());
console.log(fib.next());
console.log(fib.next());
console.log(fib.next());
console.log(fib.next());
console.log(fib.next());
console.log(fib.next());
console.log(fib.next());
console.log(fib.next());
console.log(fib.next());
console.log(fib.next());

使用生成器来编写无限的斐波拉契数列可以很好的进行代码编写,没有那么多复杂的过程。

2. 传递参数

生成器函数传递参数主要通过三个实例方法next、throw、return

next在生成器中被用来继续执行生成器函数知道遇到下一个yield停止,如果函数执行完了,返回值done就为true了

关于next传递参数的问题,我这里引用阮一峰老师的ES6 入门教程,next的参数是设置上一个yield的结果,yield本身并没有结果,举个例子:

function *test() {
  let info = yield 1;
  console.log(info);
  info = yield 2 + info;
  console.log(info);
}
const gen = test();
console.log(gen.next());
console.log(gen.next(2));
console.log(gen.next(5));

解释一下:

gen的第一个next不能传递参数,就算传递参数也没用,因为next传参是设置上一个yield的结果

第二个next传递参数2,是设置第一个yield的结果,则info=2,函数执行到第二个yield时,函数暂停,yield向外抛出一个4

第三个next传递参数5,是设置第二个yield的结果,则info为5,函数执行完毕返回值为undefined

 throw和return就比较好理解了,throw用来抛出一个错误到生成器,return用于提前终止生成器函数

跟Promise的结合使用

在async和await没有发布之前,yield常备用来和Promise一起用,达到和async相同的效果,比如说模拟fetch异步请求数据

const p = new Promise((resolve, reject) => {
  setTimeout(() => resolve({
    json() {
      return { name: "john" };
    }
  }), 5000);
});
function* task() {
  const d = yield p;
  console.log(d);
  const e = yield d.json();
  console.log(e);
}

function runTask(genFunc) {
  const gen = genFunc();
  let result = gen.next();
  const handleResult = () => {
    if (result.done) {
      return;
    }

    if (typeof result.value.then === "function") {
      result.value.then(data => {
        result = gen.next(data);
        handleResult();
      })
        .catch(err => {
          result = gen.throw(err);
          handleResult();
        });
    } else {
      result = gen.next(result.value);
      handleResult();
    }
  }

  handleResult();
}

runTask(task);

以上就是我对于生成器的理解,非常nice,你学费了吗?

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值