26
回答
高手问答第 200 期 — 兼具函数式和响应式编程的 RxJS,了解一下?

OSCHINA 本期高手问答(2018 年 6 月 5 日 — 6 月 11 日)我们邀请到了程墨老师@程墨Morgan 和大家一起讨论关于 RxJS 的问题。

程墨,资深架构师,曾任职于摩托罗拉、雅虎和微软,云鸟配送平台联合创始人,目前服务于美国视频服务公司 Hulu。

技术发展迅速,用户的需求增加更快,软件的代码库也会随需求增长快速膨胀,在这种情况下,如何保证代码质量?如何控制代码的复杂度?如何维持代码的可维护性?就成了软件开发的大问题。业界的同仁们为了解决这些老问题做了各种努力,函数式编程和响应式编程就是在实践中被证明行之有效的两种方法。

RxJS 兼具函数式和响应式两种编程方式的特点,RxJS 擅长处理异步操作,因为它对数据采用“推”的处理方式,当一个数据产生的时候,被推送给对应的处理函数,这个处理函数不用关心数据是同步产生的还是异步产生的,这样就把开发者从命令式异步处理的枷锁中解放了出来。

本期高手问答内容:

  1. RxJS入门​​​​​​​
  2. RxJS 开发实践
  3. 函数式和响应式编程

为了鼓励踊跃提问,@华章 会在问答结束后从提问者中抽取 5 名幸运会员赠予《深入浅出RxJS》一书。

购买链接:https://item.jd.com/12336101.html

OSChina 高手问答一贯的风格,不欢迎任何与主题无关的讨论和喷子。

下面欢迎大家就 RxJS 方面的问题向程墨老师@程墨Morgan 提问,请直接回帖提问。

举报
局长
发帖于4个月前 26回/2K+阅
共有26个答案 最后回答: 3个月前

OSC 第 200 期高手问答 -- 兼具函数式和响应式编程的 RxJS,了解一下?

@quanwei9958    @devbean-豆子    @jianxia84​​​​​​​   @htoooth​​​​​​​   @龙泰勒​​​​​​​

恭喜以上五位网友获得深入浅出RxJS一本

请私信@华章 告知快递信息(格式:姓名+电话+地址+邮编)

--- 共有 1 条评论 ---
程墨Morgan恭喜得奖的网友,谢谢大家的参与。 3个月前 回复

@程墨Morgan

使用RxJS时,我有一些困惑,望解答。

 

以下面这个例子来说明。

一堆url,拿每个url 发送请求,获取结果,分析,入库。

const fetchContent = url => Rx.Observable.interval(Math.random() * 1000).take(1).mapTo(`${url}\nPrice: ${(10 * Math.random()).toFixed(2)}`);
const getPrice = content => (/Price: ([\d.]+)/).exec(content)[1];
const saveToDb = price => Rx.Observable.interval(Math.random() * 200).take(1).mapTo(`${price} save to db`);

Rx.Observable.from(['http://example.com/1', 'http://example.com/2', 'http://example.com/3', 'http://example.com/4'])
    .mergeMap(fetchContent)
    .map(getPrice)
    .concatMap(saveToDb)
    .subscribe(console.log, console.error);

这段代码现在看起来挺清晰的。mergeMap(fetchContent) 保证了请求是一起发出去的。

 

现在来了个新需求,saveToDb要求保存url的信息,就麻烦了。因为在fetchContent之后,url的信息在后续流中已经没有了。

为了满足需求,我得组合流、或者在流中一直传递url信息、或者利用闭包。

以组合流为例:

const fetchContent = url => Rx.Observable.interval(Math.random() * 1000).take(1).mapTo(`${url}\nPrice: ${(10 * Math.random()).toFixed(2)}`);
const getPrice = content => (/Price: ([\d.]+)/).exec(content)[1];
const saveToDb = ([url, price]) => Rx.Observable.interval(Math.random() * 200).take(1).mapTo(`${url} ${price} save to db`);

const url$ = Rx.Observable.from(['http://example.com/1', 'http://example.com/2', 'http://example.com/3', 'http://example.com/4']);
const result$ = url$.map(fetchContent).combineAll().switchMap(Rx.Observable.from).map(getPrice);
url$.zip(result$).concatMap(saveToDb).subscribe(console.log, console.error);

为了保证请求是一起发出去的,结果是有序的,result$就显得有点复杂。

 

如果类似的流程很多,都这么去做,可读性越来越不好,是否是我使用姿势不对?

--- 共有 5 条评论 ---
apolis 回复 @程墨Morgan : 印证了“科学发展到最后就是哲学” :D 如果没有十全十美,个人倒是更喜欢组合流的方式。一直向下传递让其他流变的没那么纯粹了,有点被污染了的感觉。 4个月前 回复
程墨Morgan 回复 @apolis : 上帝很公平,没有十全十美的解决方法,必须取舍。 4个月前 回复
apolis 回复 @程墨Morgan : 嗯,这样可以继续保持简洁。但下游流的处理流程都要修改。在实际项目中,改动很多,出bug的概率比较大。 4个月前 回复
程墨Morgan我建议这么弄,fetchContent产生的stream中数据包含两个property的对象,一个是content,一个是url,只需要一个map就能搞定,这样,在下游的数据流中都能同时获得url和content。 4个月前 回复
apolis就这个例子再进一步: 考虑每个url请求数据,获取结果,分析,入库这任一过程都可能出错,希望一个url失败不影响别的,又会让代码显得更复杂。 感觉操作符的灵活性太大了,每个人都可能用不同的方式组合出想要的结果。 应该避免操作符哪些使用方式(出于性能的考虑,或代码清晰度的考虑),有没有好的经验分享? 4个月前 回复

@程墨Morgan 程老师好,非常感谢有机会交流。

我一直有疑问,rxjs 的应用场景在哪里?因为正常情况下,js 中异步的问题都可以使用 bluebird 进行解决。是现在没有合适的业务场景去应用呢?,还是说 rxjs 只是一种问题解决思路,并不跟业务场影有关,现有代码就可以去使用,那这样是不是就陷入了拿着锤子看什么都是针子的认知错误中?

还有 rxjs 最重要的是对“背压”的处理,能不能结合您的工作来谈谈,您是在什么具体情况下怎么使用的?

--- 共有 4 条评论 ---
程墨Morgan当上游的数据产生速度超过下游的数据处理速度,就需要背压控制了,其实背压控制的场景很多,最常见的就是mousemove和scroll事件的处理,就需要背压。 4个月前 回复
程墨Morgan不是所有场景都适合于用RxJS,因为RxJS学习取消很陡,至少一个hello world没必要用RxJS。 4个月前 回复
程墨Morgan你所说的bluebird,只是Promise的一种实现,所以我想你说的是Promise能够解决很多场景问题。 Promise当然也不错,比RxJS好懂很多,但是Promise也有一些缺点,比如Promise只能有一个结果,Promise不能被cancel,而RxJS对可以克服这些问题。 4个月前 回复
程墨Morgan前面回答过rxj的应用场景问题,copy paste过来: RxJS适用于有复杂数据操作的前端应用,尤其是很多异步操作的应用,实际中这样的应用还真不在少数。 4个月前 回复

@程墨Morgan 您好

目前在Angular2里面使用了Rxjs,目前迁移到了Angular6。Rxjs的用法也产生了非常大的变化,甚至提出了一个兼容层。

当前我再Angular里的处理场景,主要使用在:组件内以及跨组件的消息订阅分发(例如需要和父组件互动,平级组件互动的情况下)

当前有一些疑问:1、Rxjs是否适合这样的场景?2、Rxjs新版有哪些大的变化,大到需要兼容层?

--- 共有 5 条评论 ---
雪舞潇湘 回复 @Raphael_goh : 谢谢指导。看来目前的用法也没有太大的问题~另外我也需要逐步迁移到6.x版本上来了。目前的兼容方案也不是很长久~ 3个月前 回复
Raphael_goh顺便说一句,虽然5.5已经提供了类似的功能,但这只是为了6.x的一个缓冲,5.5里包含了两块代码,而6.x则是做了彻底的拆分,意思是改造完毕了rxjs-compat就可以彻底不要了,可以节省更多的资源 4个月前 回复
Raphael_goh2. 你说的是rxjs5.x和6.x的区别,因为rxjs5.x是es5的,尤其是在es6 module盛行的现在,5.x完全无法被tree shaking, 所以才重写了。rxjs-compat的作用就是兼容非模块化的写法才提供的,但这只是为了过渡,项目里迟早要全部改成模块化的方式 4个月前 回复
Raphael_goh1. angular引入rxjs的目的就是为了解决你说的跨组件之间的通信问题,尤其是消除两个组件之间的依赖关系,和发布订阅这样的场景 4个月前 回复
程墨Morgan1. RxJS适合这样的数据传递逻辑; 2. 我对Angular不是很了解,具体指的是RxJS哪个版本和哪个版本的比较? 4个月前 回复

@程墨Morgan 程老师您好。

接触Rxjs 还是在angular里,感觉一切交互都可以用Rxjs的响应式编程,一切都可以变成流(时间、表单、指令、服务),感觉非常方便的订阅数据,但是国内的Rxjs的文档的确不太多,我阅读过Rxjs得中文文档,感觉还是很吃力,希望以后能多渠道的推广Rxjs响应式编程,中文教程能更细致一些就好了。

--- 共有 1 条评论 ---
程墨MorganRxJS中文文档读起来比较吃力,是因为那是从英文文档翻译过来的,而英文文档本身结构和内容也存在问题。 我写《深入浅出RxJS》的初衷之一,就是觉得RxJS官方文档的结构不好,不是线性的学习过程,所以门槛很高,希望读了这本书能够帮助到你。 4个月前 回复

@程墨Morgan

响应式编程有什么应用场景?

--- 共有 3 条评论 ---
程墨Morgan你用过Excel吧,用Excel的SUM公式来统计你这个月花了多少钱,然后在每个格子里填今天花了多少钱,在一个格子里自然就”响应“显示总数,同时,这个总数的格子也可以用来作为输入计算你今年花了多少钱。如果你感觉这样的设计很方便,就可以理解响应式编程的好处了。 4个月前 回复
一个灰省略1w字有点不负责任,游戏的应用场景最合适,因为游戏的事件比较多,逻辑比较复杂,交互比较多。平时写程序,你需要在一个事件里面写很多判断和逻辑功能,RxJS可以让你在多个事件流组合的结果里面写一个逻辑功能。修改逻辑功能就变的十分简单,排错也更容易。代码阅读起来更符合人的思维,减少精神分裂 4个月前 回复
一个灰事件驱动的场景都可以用,优势非常多,此处省略1w字 4个月前 回复

@程墨Morgan 您好,请问对于像我这类没用过RxJS的新手,如果产生对该技术的价值认同?

--- 共有 11 条评论 ---
LeoXu 回复 @程墨Morgan : 了解了,谢谢 4个月前 回复
程墨Morgan 回复 @LeoXu : 利弊我在书中有详细解释,简单说来,RxJS适合于需要复杂异步操作的应用,不过就像我说的,RxJS只是工具,如果你觉得自己的工作并不需要使用这个工具,完全不需要去认同它,知道有这么个东西就足够。 4个月前 回复
LeoXu 回复 @程墨Morgan : 呃,是的,是错别字。就是想问问这个工具如何让像我这样的新手提起兴趣去用它,对于机械键盘而言,认为某种轴能让打字更具效率,或者游戏体验更好,应该就是在使用价值上认同了才会去用。或者您也可以简单讲讲用这个工具的利弊,比如使用过程中可能会有什么关键性的问题要处理。 4个月前 回复
程墨Morgan是不是想问”如何产生对该技术的价值认同“? 我个人观点,对于技术有啥价值认同可言,技术只是工具,谁会对工具产生什么价值认同呢?一个工具也不是适合所有人,有的人喜欢用红轴键盘,有的人喜欢用青轴键盘,有的人喜欢用薄膜键盘,都是觉得那个键盘趁手,只有喜好,不涉及到价值观。 4个月前 回复
LeoXu@一个灰 回复@一个灰 : 多谢交流 4个月前 回复

@程墨Morgan

为什么只有angular内置了rxjs(必选项),  (而vue对rxjs是可选项).  angular的http模块相比其它框架(不用rxjs直接用axios) 有什么优势, 我看使用rxjs的angular代码长度明明是增加了, 并没有简化http? :D

--- 共有 1 条评论 ---
程墨MorganAngular是一个框架,所谓框架,就是不光提供函数被你调用,还规定了你的应用应该怎么写。 而React、Vue、RxJS更多是一个库(library),并不规定你的应用的结构。 我个人不大喜欢Angular要求使用RxJS的做法,因为这样把学习门槛提高了。 4个月前 回复
请问响应式编程和一般编程有什么区别?还有这个与rxjava哪些api一样吗?
--- 共有 1 条评论 ---
程墨Morgan响应式编程中,数据是被”推“,而不是被”拉“,你写的大部分代码知识在被动”响应“数据的推送,而不用关心数据如何被分配处理。 Rx是一个概念和一套API,各种语言都有实现,各种语言之间的API也比较想通,当然也肯定会有些不一样,比如RxJava会涉及到多线程,而RxJS因为在JS环境所有没什么多线程可言。 4个月前 回复
顶部