ECMAScript 6 学习系列课程 (ES6 Generator 函数的使用)

23 篇文章 0 订阅

这里写图片描述

在学习ES6的过程中,个人认为Generator稍微会有些难以理解,其实Generator函数是ES6提供的一种异步编程解决方案,语法行为与传统函数完全不同。

我们从表面上看,Generator函数是一个普通函数,但是有两个特征:
1. function关键字与函数名之间有一个星号;
2. 函数体内部使用yield语句,定义不同的内部状态(yield语句在英语里的意思就是“产出”)。

这里推荐给大家一个在线的测试工具,方便我们学习测试使用:

http://www.es6fiddle.net

我们来看一段简单的使用代码:

function* helloWorldGenerator() {
  yield 'hello';
  yield 'world';
  return 'ending';
}

var hw = helloWorldGenerator();

console.log(JSON.stringify(hw.next()));
console.log(JSON.stringify(hw.next()));
console.log(JSON.stringify(hw.next()));

上面代码中,我们定义了一个hello world的生成函数,从代码上看出,generator的语法定义主要有一个* 号,外加yield语法。

当我们调用helloWorldGenerator(), 并没有真正返回给我们加过,而是在pending的状态,只有当我们调用了next()语法之后,才看到打印结果。

另外需要注意,yield语句不能用在普通函数中,否则会报错。

这就是generator的基本使用。

使用Iterator遍历Generator函数:

var myIterable = {};
myIterable[Symbol.iterator] = function* () {
  yield 1;
  yield 2;
  yield 3;
};

[...myIterable] // [1, 2, 3]

next方法的参数

之前我们使用过next()函数,但是并没有传递任何参数,其实通过传参地一定的参数,就有办法在Generator函数开始运行之后,继续向函数体内部注入值。也就是说,可以在Generator函数运行的不同阶段,从外部向内部注入不同的值,从而调整函数行为, 我们可以看一段代码:

function* foo(x) {
  var y = 2 * (yield (x + 1));
  var z = yield (y / 3);
  return (x + y + z);
}

var a = foo(5);
a.next() // Object{value:6, done:false}
a.next() // Object{value:NaN, done:false}
a.next() // Object{value:NaN, done:true}

var b = foo(5);
b.next() // { value:6, done:false }
b.next(12) // { value:8, done:false }
b.next(13) // { value:42, done:true }

对于yield方法,如果我们没有传递一个参数在next()方法中,yield的返回会是undefined, 这也就是为什么我们会看到value: NaN的结果。

再来看看传递参数后的结果, 首先我们一定要先传递一个参数,否则第一次运行x参数会报错误。

所以当第一次运行next()时候会看到 x=1 , 也就是5+1=6.

当第二次运行next(12), 传递一个参数12, 这个12会赋值给(yield (x + 1)), 也就是y的数值此时已经变为2 * 12 ,也就是24,在执行24/3, 就是第二次运行的结果:8 。

同理,第三次运行next(13), 传递一个参数13, 这个13会赋值给yield (y / 3), 此时的z也就等于13, 所以最后一次next的运行结果为13+24+5=42.

通过上面这个例子,大家可以简单理解其原理。

for …. of循环遍历Generator函数

其实遍历Generator函数的方法有很有,for… of就是其中一个,我们看下代码就明白了:

function *foo() {
  yield 1;
  yield 2;
  yield 3;
  yield 4;
  yield 5;
  return 6;
}

for (let v of foo()) {
  console.log(v);
}
// 1 2 3 4 5

常见的应用场景:

如果有一个多步操作非常耗时,采用回调函数,可能会写成下面这样。

step1(function (value1) {
  step2(value1, function(value2) {
    step3(value2, function(value3) {
      step4(value3, function(value4) {
        // Do something with value4
      });
    });
  });
});

采用Promise改写上面的代码。

Q.fcall(step1)
  .then(step2)
  .then(step3)
  .then(step4)
  .then(function (value4) {
    // Do something with value4
  }, function (error) {
    // Handle any error from step1 through step4
  })
  .done();

Generator函数可以进一步改善代码运行流程:

function* longRunningTask() {
  try {
    var value1 = yield step1();
    var value2 = yield step2(value1);
    var value3 = yield step3(value2);
    var value4 = yield step4(value3);
    // Do something with value4
  } catch (e) {
    // Handle any error from step1 through step4
  }
}

scheduler(longRunningTask());

function scheduler(task) {
  setTimeout(function() {
    var taskObj = task.next(task.value);
    // 如果Generator函数未结束,就继续调用
    if (!taskObj.done) {
      task.value = taskObj.value
      scheduler(task);
    }
  }, 0);
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

天外野草

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值