专栏名称: 高效运维
高效运维公众号由萧田国及朋友们维护,经常发布各种广为传播的优秀原创技术文章,关注运维转型,陪伴您的运维职业生涯,一起愉快滴发展。
目录
相关文章推荐
InfoQ架构头条  ·  麦当劳中国:打造 MACH 架构的数字化巨无霸 ·  4 天前  
51好读  ›  专栏  ›  高效运维

使用 Rx.JS 优化 Fetch API

高效运维  · 公众号  · 运维  · 2017-03-01 07:15

正文

处理用户客户端(浏览器)与后端服务器的数据交互(一般通过 RESTful API)是前端开发最基本的问题之一。在浏览器里增删改查服务器端资源时,当前最普遍采用的解决方案是基于 Ajax 的回调或 Promise 。



Fetch API 的问题


以 Fetch API 为例,想要获取一个位于http://api/v1/的资源foo(id = 123),可以生成资源 URI 如http://api/v1/foo/123,将所需的参数构造好,最后发送 HTTP 请求fetch('http://api/v1/foo/123'),结果作为 Promise返回。


对于业务单一的简单Web应用例如新闻 Feed网站,这样的调用模式足够了。但对于高响应式的复杂Web应用例如 SaaS 控制台,可能存在两个很重要的需求:


  • 缓存。对获取过的数据,可以先返回缓存的数据,而不去调用 API

  • 数据更新通知。如果客户端或是服务器端更新了数据,需要通知到资源使用者



基于资源特征(URI)的缓存机制


缓存的一种解决方案是,将 Fetch API 简单封装,使得在 HTTP API 调用层可以针对每次请求的特征(比如URI、请求参数、请求方法等)进行缓存。这种方式初看思路简单,对现有系统的侵入性也不大,但实际使用上问题较多。


上述缓存方式的问题在于,客户端代码对服务器资源的调用方法过于透明,缺少资源的抽象,导致增删改操作的副作用不好控制,往往使得处理缓存的逻辑变得很复杂。举个例子,资源 foo(id=123) 可能存在于多个 RESTful 路径上,比如/foo (foo资源的列表) 和/foo/123。缓存该资源查询后,针对 foo(id=123) 的每次更新操作都需要枚举所有 URI 进行 Cache busting。


有的同学可能就说了,是不是可以在获取某个资源的时候,有缓存就立刻返回缓存的数据,同时重新调用一次 API,当数据更新时再返回新数据。这种思路在我看来是种解决方案,但HTTP返回值是个 Promise,实现起来会十分别扭。Promise 实际上是对于未来某个可能发生结果(也就是回调函数)的抽象,且结果的返回是一次性的,就导致它灵活性不足。



使用 RxJS 封装 Fetch API


下面是经过 Rxjs 封装过的 Fetch request:



使用上,针对某种特定的资源 URI 必须要有一个单例FetchDataStore来封装它的 Fetch 过程。


举一个例子,对于应用内的资源foo(id=123),我们可以这样封装它:



跟 Promise 版本相比,封装后的 Fetch datastore 有几个好处:


不过,上面的解决方案还是需要用户在合适的时机调用refetch函数,保证多个观察者的数据能一直处在最新状态。



更进一步


作者粗略的扫了一下 teamabition 的基于 Rxjs 的数据同构 SDK 。跟上面简单的 Fetch API 相比,teambition SDK 需要使用者引入下面两个规则:


Model Schema: 任意资源需要有自己独立的标示方式,并且保证在不同的上下文中保持 Schema 一致。使用 Schema 通常意味着需要引入一种类型系统,目前最常见的是 TypeScript 。


资源的数据交互都是通过封装的 Model 接口进行,避免直接调用 API。


加上数据模型抽象后,就可以在大多数情况下避免主动调用API,简化 View 层逻辑。


另外要时刻谨记:系统越复杂,出问题的可能性越大。保持系统简单高效是最好的。



本文作者:灵雀云-肖鹏



灵雀云 释放云的无限潜能
长按,识别二维码,加关注




点击“阅读原文”,了解灵雀云