如何搭建数据中间层的笔记和思考

为了理清楚复杂的业务数据,我们通常会在组件之上,在视图渲染之下建立数据中间层。

数据中间层的意义

  • 视图层之间的数据共享
  • 避免组件的过度封装
  • 兼容服务端推送,异步获取,本地缓存
  • 数据聚合

搭建数据中间层的难点

Promise, Generator , Reactive

我们用 react ,用 redux 做数据状态管理。数据有同步有异步,前端大量的工作在于处理异步数据。通常有几种方式:

  • 事件和回调
  • Promise
  • Generator
  • Async & Await

再总结下实际数据处理中存在的问题:

  • 同一种数据,可能来自同步缓存,可能需要异步取
  • 数据可能需要更新(推送),获取数据和更新数据写法不同
  • 渲染数据需要对查询数据组合,组合逻辑 reducer 分组也不能解决

一些对比

统一同步和异步 Promise 处理同步和异步 (用resolve 把同步转成异步)

Javascript
function fetchP() {
	if (data) {
		return Promise.resolve(data)
	} else {
		return ajax.get()
	}
}
fetchP().then(data => data)

Reactive 处理同步和异步

Javascript
function fetchO() {
	if (data) {
		return Observable.of(data)
	} else {
		return Observable.fromPromise(ajax.get())
	}
}
fetchO().subscribe(data => data)

Reactive 的优点在于 subscribe 可以响应多次 Promise 的有点在于编写简单

统一获取和订阅的逻辑 对于某一个数据的更新,其实我们不需要知道它是直接获取的,还是被动推送而更新的,我们只需要知道它变了。所以Reactive 响应多次的优点就有用武之地了。

同样做对比: Promise 实现获取的订阅:

Javascript
worker.on('data', data => {})
fetchP()

Reactive 实现获取的订阅:

Javascript
fetchO().subscribe(data => data)

Promise 需要区分 获取 和 订阅 Reactive 则直接描述了一个统一的业务过程:数据有变

数据组合

组合有多重情况

  • 多条数据实体(可能异步可能同步),在渲染之前需要组合 -> 制定组合规则
  • 已经组合好的数据,有新数据源到来(推送),需要重新组合 -> 组合规则重新走一遍

Reactive 制定一套组合规则,并不需要考虑数据是获取的or 推送的。

按需获取 使用 Promise 、或者接受变动进而主动更新状态的做法,往深层次想都不是按需操作。因为更新的状态可能并不完全需要显示,很可能会有更新的状态并不需要显示(或者我们很难控制没有副作用的更新)。

比如 数据实体 a 变了,我们需要把所有引用到 a 的变量都更新一遍,比如因此更新了 数据实体 c,但数据实体 c 此时是不需要显示的,这样就是副作用。

但 Reactive ,是从使用者的角度看问题,只会对订阅的数据更新。 如果数据实体 a 变了,引用了 a 的数据实体 c, 此时并没有被订阅,他并不会产生更新,不需要走可能很复杂的一套组合规则。

如何选方案

Promise 、Generator 和 Async Await 关注点是流程控制,像写同步一样的写异步目的之一就是为了让整个异步流程可控方便追踪。 事件和回调关注点是消息通知,比如基于事件和回调的订阅接收模型,前端有大量的消息需要逆异步流程传递(随意传递)。

随之而来的问题就是:

  • 用消息通知的时候,遇到大规模且复杂的异步操作,代码完全乱了,难以维护。
  • 用流程控制的时候,针对随意传递的消息,我们会制定更多的流程,让这些消息传递也成为一个可控的流程。

所以,对大规模复杂应用,用 Promise 、Generator 和 Async Await 们控制流程比事件回调更合适,但制定更多的流程规则会带来代码『人工复杂度』(重复劳动啊)上的问题。

比如我们 redux-thunk 处理异步,越写到后面越难受,一个简单的业务逻辑很可能会需要写出好几套 reducer 和 action。redux-thunk 就是基于 promise 的。

相应的,redux-saga 是基于 Generator 的,蚂蚁为了简化流程制定,在 redux-saga 基础上搞了 dva。

另一个思路是 Reactive,很多人说 Reactive 是综合了流程控制和消息通知。我理解 Reactive 的重点是两个,响应和需求。

用所谓的响应式 Reactive 编程,我们不关注什么流程、什么消息,我们只需要关注针对什么刺激需要出哪些响应结果。Reactive 的库 rxjs 提供的各种流程操作并不是重点,重点是为了让人不要关心过程,而是关心刺激和响应的结果。这是一种完全不同的思路。

相应的,redux-observable 是基于 Reactive(rxjs) 的。

用 Reactive 的理念管理数据就会变成这样:

  • 根据渲染需求确定所有的数据流,描述每个数据流从获取到输出的全过程。。之后不用再管它。。

完全,直接按需获取。。不需要考虑数据变了要怎样,状态变了要怎样,怎么通知。理论上来说,这个理念似乎是最没有附加冗余的异步逻辑抽象。

但实际用起来,也还是会有问题的:

  • 代码理解成本高,用了 reactive 这一套,不方便再揉入其他思路的写法。
  • 改动难度大。现实和理论上是有差距的,一套数据流在不同的迭代中可能不同,但是reactive 的链式操作,该流程难度大(它本来就不关心流程。。)。
  • 也许还有其他。

所以选哪一套方案,是个难有结论的问题。

  • 如果可以合理控制,降低流程制定的繁琐程度,用 redux-saga
  • 如果异步数据流实在太多,异步太过复杂,用 redux-observable