RxJS的另外四种实现方式(五)——使用生成器实现

接上一篇RxJS的另外四种实现方式(四)——性能最高的库(续)

js的生成器一般情况下使用场景很少,开发者接触的不是很多。不了解的可以先行查看js语法了解。

这里把其中的执行顺序图解一下

1调用方 数据源 2next(value)---------------------------------------> 开始执行生成器函数体 3 <-------------------------------------------------yield value2 4next(value3)---------------------------------------> 5 <-------------------------------------------------yield value4 6next(value5)---------------------------------------> 7 <-------------------------------------------------return value6

以上是正常返回最后值的过程,也可以永远不return,变成一个无限生成数据的过程。 另一种情况是提前终止

1调用方 数据源 2next(value)---------------------------------------> 开始执行生成器函数体 3 <-------------------------------------------------yield value2 4next(value3)---------------------------------------> 5 <-------------------------------------------------yield value4 6return()--------------------------------------->

这种情况下相当于主动关闭生成器。 可以向数据源的函数发出错误:

1调用方 数据源 2next(value)---------------------------------------> 开始执行生成器函数体 3 <-------------------------------------------------yield value2 4next(value3)---------------------------------------> 5 <-------------------------------------------------try catch 6throw(err)

以上各种行为都可以对应Rx,那么生成器和Rx的最大区别是什么呢?

就是谁是主动方,谁是被动方。在生成器中,调用方是主动方,相当于主动pull数据,而Rx中,数据源是主动方,相当于主动push数据。(这里和Rx中的推拉模式有区别)

那么如何使用生成器实现Rx呢?其实你估计已经想到了,就是反过来即可:

1Observable Observer 2next(value)---------------------------------------> 开始执行生成器函数体 3 <-------------------------------------------------yield value2(得到返回值是value3) 4next(value3)---------------------------------------> 5 <-------------------------------------------------yield value4(返回值是value5) 6next(value5)---------------------------------------> 7 <-------------------------------------------------return value6() 8done==true

于是我们就得到了由Observable主动推送过来的数据了。

我们还是以interval举例

1exports.interval = period => sink => { 2 if (sink.next().done) return noop 3 let i = 0; 4 const id = setInterval(() => sink.next(i++).done && clearInterval(id), period) 5 return () => clearInterval(id) 6}

这里传入的sink就是迭代器实例,我们主动调用next发送数据 这里我们判断了next函数的返回值里面的done属性,如果Observer主动取消订阅了(在生成器函数里面执行了return语句)那么done就为true

下面是filter操作符:

1function* _filter(sink, f) { 2 for (let done = sink.next().done; !done;) { 3 let x = yield 0 4 if (x === _done) break 5 if (f(x)) done = sink.next(x).done 6 } 7 sink.next(_done) 8 sink.return() 9} 10exports.filter = f => source => sink => source(_filter(sink, f))

_done是一个Symbol,用来表示Observable的complete事件 _filter是一个生成器,调用它时传入下一级的迭代器(Observer) yeild 0 不断获取上一级的Observable的数据,一旦收到_done,立即跳出循环,并将_done传入sink中。

最后是实现Subscriber

1function* subscribe(n, e, c) { 2 while (true) { 3 try { 4 let result = yield 0 5 while (result !== _done) { 6 if (n(result) === _done) return 7 result = yield 0 8 } 9 c && c() 10 } catch (err) { 11 e && e(err) 12 } 13 } 14} 15exports.subscribe = subscribe

是一个死循环,直到收到_done,或者抛出异常。 至此,我们的Rx的基本功能已经实现,由于生成器的性能较差,所以本人没有花很多时间去完善各种操作符,只作为一种可以实现的方式展示出来。

下一篇我们介绍最后一种实现方法。

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

RxJS的另外四种实现方式(三)——性能最高的库

接上篇RxJS的另外四种实现方式(二)——代码最小的库(续)(https://my.oschina.net/langhuihui/blog/2052019)代码最小的库rx4rxlite虽然在性能测试中超过了callbag,但和most库较量的时候却落败了,于是我下载了most库,要解开most库性能高的原因。我们先上一组测试数据,这是

RxJS的另外四种实现方式(后记)—— 同时实现管道和链式编程

目录RxJS的另外四种实现方式(序)(https://my.oschina.net/langhuihui/blog/2051754)RxJS的另外四种实现方式(一)——代码最小的库(https://my.oschina.net/langhuihui/blog/2051770)RxJS的另外四种实现方式(二)——代码最小的库(续)

RxJS的另外四种实现方式(四)——性能最高的库(续)

接上一篇RxJS的另外四种实现方式(三)——性能最高的库(https://my.oschina.net/langhuihui/blog/2054887)上一篇文章我展示了这个最高性能库的实现方法。下面我介绍一下这个性能提升的秘密。首先,为了弄清楚Most库究竟为何如此快,我必须借助其他工具。比如chrome的devtools性能分析,刚开始