You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
这样我们就能自动处理运行我们的generator了,当然我们这个很简单,没有任何错误处理,如何让多个generator同时运行,这其中涉及到如何进行控制权的转换问题。我写了一个简单的执行器Fo,其中包含了Kyle Simpson大神的一个ping-pong的例子,感兴趣的可以看下这里是传送门,当然能顺手star一下就更好了,see you next article O(∩_∩)O。
为什么要用generator
在前端开发过程中我们经常需要先请求后端的数据,再用拿来的数据进行使用网页页面渲染等操作,然而请求数据是一个异步操作,而我们的页面渲染又是同步操作,这里ES6中的
generator
就能发挥它的作用,使用它可以像写同步代码一样写异步代码。下面是一个例子,先忽略下面的写法,后面会详细说明。如果你已经理解generator
基础可以直接跳过这部分和语法部分,直接看深入理解的部分。在等待数据的过程中会继续执行其他部分的代码,直到数据返回才会继续执行
foo
中后面的代码,这是怎么实现的那?我们都知道js是单线程的,就是说我们不可能同时执行两段代码,要实现这种效果,我们先来猜想下,我们来假设有一个“王杖”(指代cpu的执行权),谁拿到这个“王杖”,谁就可以做自己想做的事,现在代码执行到foo
我们现在拿着“王杖”然后向服务器请求数据,现在数据还没有返回,我们不能干等着。作为王我们有着高尚的马克思主义思想,我们先把自己的权利交出去,让下一个需要用的人先用着,当然前提是要他们约定好一会儿有需要,再把“王杖”还给我们。等数据返回之后,我们再把我们的“王杖”要回来,就可以继续做我们想做的事情了。如果你理解了这个过程,那么恭喜你,你已经基本理解了
generator
的运行机制,我这么比喻虽然有些过程不是很贴切,但基本是这么个思路。更多的东西还是向下看吧。generator语法
generator函数
在用
generator
之前,我们首先要了解它的语法。在上面也看到过,它跟函数声明很像,但后面有多了个*
号,就是function *foo() { }
,当然也可以这么写function* foo() { }
。这里两种写法没有任何区别,全看个人习惯,这篇文章里我会用第一种语法。现在我们按这种语法声明一个generator
函数,供后面使用。yield
到目前为止,我们还什么也干不了,因为我们还缺少了一个重要的老伙计
yield
。yield
翻译成汉语是产生的意思。yield
会让我们跟在后面的表达式执行,然后交出自己的控制权,停在这里,直到我们调用next()
才会继续向下执行。这里新出现了next
我们先跳过,先说说generator
怎么执行。先看一个例子。下面我们来逐步分析,首先我们定义了一个
generator
函数foo
,然后我们执行它foo()
,这里跟普通函数不同的是,它执行完之后返回的是一个迭代器,等着我们自己却调用一个又一个的yield
。怎么调用那,这就用到我们前面提到的next
了,它能够让迭代器一个一个的执行。好,现在我们调用第一个it.next()
,函数会从头开始执行,然后执行到了第一个yield
,它首先计算了1 + 1
,嗯,然后停了下来。然后我们调用第二个it.next(2)
,注意我这里传入了一个2
作为next
函数的参数,这个2
传给了a
作为它的值,你可能还有很多其他的疑问,我们详细的后面再说。接着来,我们的it.next(2)
执行到了第二个yield
,并计算了2 + a
由于a
是2
所以就变成了2 + 2
。第三步我们再调用it.next(4)
,过程跟上一步相同,我们把b
赋值为4
继续向下执行,执行到了最后打印出我们的b
为4
。这就是generator
执行的全部的过程了。现在弄明白了yield
跟next
的作用,回到刚才的问题,你可能要问,为什么要在next
中传入2
和4
,这里是为了方便理解,我手动计算了1 + 1
和2 + 2
的值,那么程序自己计算的值在哪里?是next
函数的返回值吗,带着这个疑问,我们来看下面一部分。next
next的参数
next
可以传入一个参数,来作为上一次yield
的表达式的返回值,就像我们上面说的it.next(2)
会让a
等于2
。当然第一次执行next
也可以传入一个参数,但由于它没有上一次yield
所以没有任何东西能够接受它,会被忽略掉,所以没有什么意义。next的返回值
在这部分我们说说
next
返回值,废话不多说,我们先打印出来,看看它到底是什么,你可以自己执行一下,也可以直接看我执行的结果。执行结果:
看到这里你会发现,
yield
后面的表达式执行的结果确实返回了,不过是在返回值的value
字段中,那还有done
字段使用来做什么用的那。其实这里的done
是用来指示我们的迭代器,就是例子中的it
是否执行完了,仔细观察你会发现最后一个it.next(4)
返回值是done: true
的,前面的都是false
,那么最后一个打印值的undefined
又是什么那,因为我们后面没有yield
了,所以这里没有被计算出值,那么怎么让最后一个有值那,很简单加个return
。我们改写下上面的例子。执行结果:
最后的
next
的value
的值就是最终return
返回的值。到这里我们就不再需要手动计算我们的值了,我们在改写下我们的例子。大功告成!这些基本上就完成了
generator
的基础部分。但是还有更多深入的东西需要我们进一步挖掘,看下去,相信你会有收获的。深入理解
前两部分我们学习了为什么要用
generator
以及generator
的语法,这些都是基础,下面我们来看点不一样的东西,老规矩先带着问题才能更有目的性的看,这里先提出几个问题:迭代器
进行下面所有的部分之前我们先说一说迭代器,看到现在,我们都知道
generator
函数执行完返回的是一个迭代器。在ES6中同样提供了一种新的迭代方式for...of
,for...of
可以帮助我们直接迭代出每个的值,在数组中它像这样。下面我们用我们的
generator
迭代器试试现在我们发现
for...of
会直接取出我们每一次计算返回的值,直到done: true
。这里注意,我们的4
没有打印出来,说明for...of
迭代,是不包括done
为true
的时候的值的。下面我们提一个新的问题,如果在
generator
中执行generator
会怎么样?这里我们先认识一个新的语法yield *
,这个语法可以让我们在yield
跟一个generator
执行器,当yield
遇到一个新的generator
需要执行,它会先将这个新的generator
执行完,再继续执行我们当前的generator
。这样说可能不太好理解,我们看代码。这里有两个
generator
我们在bar
中执行了foo
,我们使用了yield *
来执行foo
,这里的执行顺序会是yield 1
,然后遇到foo
进入foo
中,继续执行foo
中的yield 2
直到foo
执行完毕。然后继续回到bar
中执行yield 5
所以最后的执行结果是:异步请求
我们上面的例子一直都是同步的,但实际上我们的应用是在异步中,我们现在来看看异步中怎么应用。
这里又回到一开头说的那个例子,异步请求在执行到
yield
的时候交出控制权,然后等数据回调成功后在回调中交回控制权。所以像同步一样写异步代码并不是说真的变同步了,只是异步回调的过程被封装了,从外面看不到而已。错误处理
我们都知道在js中我们使用
try...catch
来处理错误,在generator中类似,如果在generator
内发生错误,如果内部能处理,就在内部处理,不能处理就继续向外冒泡,直到能够处理错误或最后一层。内部处理错误:
外部处理错误:
在
generator
的错误处理中还有一个特殊的地方,它的迭代器有一个throw
方法,能够将错误丢回generator
中,在它暂停的地方报错,再往后就跟上面一样了,如果内部能处理则内部处理,不能内部处理则继续冒泡。内部处理结果:
外部处理结果:
根据测试,发现迭代器的
throw
也算作一次迭代,测试代码如下:当用
throw
丢回错误的时候,除了try
中的语句,迭代器迭代掉了yield 3
下次再迭代就是,就是最后结束的值了。错误处理到这里就没有了,就这么点东西^_^。自动运行
generator
能不能自动运行?当然能,并且有很多这样的库,这里我们先自己实现一个简单的。这样我们就能自动处理运行我们的
generator
了,当然我们这个很简单,没有任何错误处理,如何让多个generator
同时运行,这其中涉及到如何进行控制权的转换问题。我写了一个简单的执行器Fo
,其中包含了Kyle Simpson大神的一个ping-pong的例子,感兴趣的可以看下这里是传送门,当然能顺手star一下就更好了,see you next articleO(∩_∩)O。参考链接
The text was updated successfully, but these errors were encountered: