【前端进阶】 ES6之“生成器”学习+ 生成器和Promise的结合使用

本文详细介绍了ES6中的生成器,包括它们如何打破函数的完整运行,实现输入和输出,以及如何与Promise结合使用。通过示例展示了生成器如何在异步操作中扮演关键角色,允许同步式的错误处理,并通过`run()`工具函数实现对多个Promise的并发处理。
摘要由CSDN通过智能技术生成

前言:

在这里插入图片描述
ES6生成器是一种可以实现看似同步的异步流程控制表达风格。

1.1 打破完整运行

在代码中几乎普遍依赖的一个假定:一个函数一旦开始执行,就会运行到结束,期间不会有其他代码能够打断它并插入其间。
这是个错误的假定。ES6引入了一个新的函数类型,它并不符合这种运行到结束的特性,这类新函数被称为生成器。

看下面代码:

var x=1;
    function *foo(){
   
      x++;
      yield;//暂停
      console.log("x:",x);
    }
    function bar(){
   
      x++;
    }

ES6代码中只是暂停点的语法是yield。运行到这里,程序会暂停。

注意:很多地方生成器的声明格式是 function* foo(){…},而不是我这里使用的function *foo(){…};唯一的区别是 * 位置的风格不同。这两种形式在功能和语法上是等同的。

看下面代码如何运行生成器:

	var it=foo();//构造一个迭代器it来控制这个生成器
	
    it.next();//这里启动foo()
    
    console.log(x);
    bar();
    console.log(x);
    it.next();

在这里插入图片描述
分析运行结果:

  1. it=foo()运算并没有执行生成器*foo(),而只是构造了一个迭代器,这个迭代器会控制它的执行。
  2. 在第一个it.next()启动了生成器*foo(),并运行了 *foo()的第一行x++。
  3. *foo()在yield语句处暂停,在这一点上第一个it.next()调用结束。此时 *foo()仍然在运行并且是活跃的,但是处于暂停状态。
  4. 我们查看x的值,此时是2 。
  5. 我们调用bar(),它通过x++再次递增x。
  6. 我们再次查看x的值,此时为3 。
  7. 最后的it.next()调用从暂停处恢复了生成器* foo()的执行,打印出x:3。

显然,foo()启动了,但是没有完整运行,它在yield处暂停了。后面恢复了foo()并让它运行到结束,但这不是必须的。

生成器就是一类特殊的函数,可以一次或多次启动和停止,并不一定非得要完成。

1.2 输入和输出

生成器是一个函数,那么它就可以接受参数也能够返回值。
看下面代码:

 function *foo(x,y){
   
      return x*y;
    }
    var it=foo(6,7);
    var res=it.next();
    console.log(res.value);//42

分析结果:

  1. it=foo(6,7);只是创建了一个迭代对象,把它赋值给一个变量it。用于控制生成器
  2. 调用it.next()来指示生成器开始继续运行,停在下一个yield处或者直到生成器结束。
  3. 这个next(…)调用的结果是一个对象,它有一个value属性。换句话说,yield会导致生成器在执行过程中发送出一个值,这有点类似于中间的return。

迭代消息传递
除了能够接受参数并提供返回值之外,生成器甚至提供了更强大更引人注目的内建消息输入输出能力,通过yield和next(…)实现。

看下面代码:

	function *foo(x){
   
    	var y=x*(yield);
    	return y;
 	  }
   	var it=foo(6);
   	it.next();
  	var res=it.next(7);
   	console.log(res.value);//42

运行结果分析:
在*foo()内部,开始执行语句var y=x…,但随后遇到了一个yield表达式。它们会在这一点暂停 *foo(),接下来调用it.next(7),这一句把值7传回作为被暂停的yield表达式的结果。即var y=6 * 7;

注意:yield和next(…)调用有一个不匹配,一般来说,需要的next(…)调用要比yield语句多一个。
为什么不匹配呢?
因为第一个next(…)总是会启动一个生成器,并运行到第一个yield处。不过,是第二个next()调用完成第一个被暂停的yield表达式,以此类推。

两个问题的故事
只考虑生成器的代码:

var y=x*(yield);
return y;

第一个yield基本上是提出了第一个问题:“这里我应该插入什么值?”。
谁来回答这个问题呢?第一个next()已经运行,使得生成器启动并运行到此处,所以显然它无法回答这个问题。因此必须由第二个next()调用回答第一个yield提出的这个问题。

看到不匹配了吗?第二个对应第一个?

我们转换一下视觉,不从生成器的视觉来看这个问题,而是从迭代器的角度。
消息是双向传递的----yield,作为一个表达式可以发出消息响应next()调用,next()也可以向暂停的yield表达式发送值。
看下面代码:

	function *foo(x){
   
     var y=x*(yield "Hello World");
     return y;
   }
   var it =foo(6);
   var res=it.next();//第一个next()并不传入任何东西
   console.log(res.value);//Hello World
   res=it.next(7);//向等待的yield传入7
   console.log(res.value);//42

yield…和next(…)这一对组合起来,在生成器的执行过程中构成了一个双向消息传递系统。

只看下面迭代器的代码:

   var res=it.next();//第一个next()并不传入任何东西
   console.log(res.value);//“Hello World”
   res=it.next(7);//向等待的yield传入7
   console.log(res.value);//42

分析:

  1. 我们并没有向第一个next()调用发送值,这是有意为之。只有暂停的yield才能接受这样一个通过next(…)传递的值,而在生成器的起始处我们调用第一个next()时,还没有暂停的yield来接受这样的一个值。所以第一个next()最好不传送值。

第一个next()调用基本上就是在提出一个问题:生成器*foo()要给我的下一个值是什么。”。谁来回答这个问题?第一个yield “Hello World”表达式。

那么第二个next(7)调用再次提出这样的问题:生成器将要产生的下一个值是什么?。但是上面代码没有yield来回答这个问题了,这该怎么处理。
return语句来回答这个问题。就算没有return语句,也会默认的return undefined来回答it.next(7)调用提出的问题。

yield和next()建立的双向消息传递是非常强大的。

1.3 生成器产生值
1.3.1 生产者与迭代器
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值