参考链接:javascript异步之async(一)
这是javascript异步系列文章的第七篇

说起javascript异步,我想你一定会想起async,甚至要排在promise之前,从今天起我们来聊一聊async 要说async,就不得不提Generator生成器(好多知识都是一环扣一环,很是无奈),async就是Generator的语法糖

Generator

关于Generator,阮一峰对此介绍的很详细,如果想深入研究可以翻阅查看,我们不在这里做详细的研究,简单了解

一个不正经的函数

Generator写法很像一个函数,但是区别于普通函数,一个栗子

  1. function doSth1() {
  2. return '这是普通函数'
  3. }
  4. const it1 = doSth1()
  5. console.log(it1); //=>这是普通函数
  6. //一个生成器doSth
  7. function* doSth() {
  8. yield '郭靖抓鸡';
  9. yield '郭靖杀鸡';
  10. yield '黄蓉拔毛';
  11. yield '黄蓉炖鸡';
  12. yield '洪七公吃鸡';
  13. return "洪七公说:这只鸡真好吃"
  14. }
  15. // 通过一个遍历器控制这个生成器的执行
  16. const it = doSth()
  17. console.log(it);

我们看下it的输出 ddq.png it是一个迭代器,我们没有像打印普通函数那样打印出我们想要的结果

普通的函数一旦执行,就不会停下来,直到执行完毕(不考虑debugger,alert,throw err,死循环等情况)

Generator生成器,可以做到,它可以让一个函数的执行暂停

Generator生成器不会自行启动,需要使用遍历器的next(),来控制Generator生成器的执行

  1. console.log(it.next());
  2. //=>{value: "郭靖抓鸡", done: false}
  3. console.log(it.next());
  4. //=>{value: "郭靖杀鸡", done: false}
  5. console.log(it.next());
  6. //=>{value: "黄蓉拔毛", done: false}
  7. console.log(it.next());
  8. //=>{value: "黄蓉炖鸡", done: false}
  9. console.log(it.next());
  10. //=>{value: "洪七公吃鸡", done: false}
  11. console.log(it.next());
  12. //=>{value: "洪七公说:这只鸡真好吃", done: true}

关于函数的return

我们打断一下,先声明一个知识点,在函数中,return不是必须存在的,但是如果我们没有为函数执行return,函数会隐式存在一个return undefined,一个栗子

  1. //不存在return,则隐式return undefined
  2. function doSth1() {
  3. let name = '这是普通函数'
  4. }
  5. const it1 = doSth1()
  6. console.log(it1); //=>undefined
  7. //存return
  8. function doSth2() {
  9. return '这是普通函数'
  10. }
  11. const it2 = doSth2()
  12. console.log(it2); //=>这是普通函数

普通函数是这样,Generator也是如此

我们继续

第一次调用next,是对Generator生成器进行启动操作,Generator生成器启动,执行,当遇到内部的yield时就会暂停 生成器中的yield有点像普通函数中的return,但是在普通函数中当遇到return,就意味着函数执行完毕,以此作为输出, 而yield不会完全停止函数的执行。

可以这么理解,Generator从一开始就是暂停的,通it.next()打破其暂停状态, 当遇到yield时,会执行yeild后面的表达式,并返回执行之后的值,然后再次进入暂停状态,

这么理解似乎更合理,

调用第一个it.next(),向Generator提出申请,我要的下一个值是什么?第一个yield,对其进行回复,就是yield ‘郭靖抓鸡’;然后Generato暂停了,等待下一次询问

然后调用第二个it.next(),向Generator提出申请,我要的下一个值是什么?第二个yield,对其进行回复,就是yield ‘郭靖杀鸡’;然后又停了

当调用第六个it.next()时,Generator已经没有yield了,怎么回答呢?它用return进行回答,终极回答

如果没有return呢,这就是我们前面说的隐式的return undefined

仔细看代码,你会发现,next和yield有一个对应关系,如果要时Generator执行完毕(done:true),next的数量要比yield的数量多一个

为什么会存在这种情况呢?

因为第一个it.next()是用来启动Generator生成器,启动后,遇到了第一个yield,停下来了,

第二个it.next()是用来启动第一个yield,以此类推,这样就导致他们“错位”了

直到Generator的return(显式或隐式)最后执行完毕

我们再看一个栗子

  1. function* doSth(params) {
  2. return params +(yield "黄蓉")
  3. }
  4. const it = doSth('郭靖')
  5. console.log(it.next())//=>{value: "黄蓉", done: false}
  6. console.log(it.next('吃饭'))//=>{value: "郭靖吃饭", done: true}

上的生成器可以这么写

  1. function* doSth(params) {
  2. let a = params +(yield "黄蓉")
  3. return a
  4. }

next() 支持传递参数,但是不会在第一个next()中传递参数,因为第一个next()是用来启动Generator的

第一个next()执行,启动了 doSth(),遇到第一个yield停下来,yield把“黄蓉”返给第一个next()

第二个next()执行,启动了第一个yield,并把“吃饭”传递给正在等待的yield,然后继续执行,遇到了return,执行完毕

今天先介绍到这里,明天继续

END