15 min/d

ぼうずやのにっき

promised-state の問題点

2016-02-06 / 2016-02-14 につくった promised-state の問題点や front-end における data store についての覚書をする。問題の存在は 2016-02-14 にも書いた。

promised-state は state を管理するための container 。state を変更するための関数 (state: State) => State | Promise<State> を渡すと state を更新してくれる。この関数はすべて直列に実行される。要するに Promise chain を管理してくれるだけのもの。

import { PromisedState } from 'promised-state';
const state = { count: 1 };
const promised = new PromisedState(state);
promised.update(({ count }) => { count: count + 1 });
promised.value()
  .then(state => console.log(state));

雰囲気だけなら redux と似ているけれど、同じように使うと問題を起こす。……というのが今回の問題点。

redux を見るとなぜ非同期処理を直列化してくれないのかと思うだろう。直列化しないのは single store で非同期処理を直列化すると、それにより全体が待たされてしまうからだ。

PromisedState はこの問題を抱えているので single store として使ってはいけない。たとえば bouzuya/virtual-dom-ssr はその anti-pattern だ。たとえば +1 button を new Promise(resolve => setTimeout(resolve, 3000) すると、click した瞬間から 3s の間まったく動かなくなる。

これを回避するには single store をやめるのが簡単だ。全体に対してこの PromisedState を適用するのではなく、state の一部分に対して PromisedState を使うと良い。

偶然なのだけど、 Qiita に Rxdux をつくったという記事があって、それについて Twitter でやりとりした。それも書いておく。

Rxdux は Redux に RxJS を使いつつ、非同期対応を加えたもの。API は違うけど PromisedState を RxJS で実装した風だ。それを single store で使う前提になっている。そもそも、ぼくが PromisedState をつくったのは RxJS だと簡単にできることが Promise だと面倒だったからなので RxJS でそれが必要なのかはよく分からないのだけど……。

single store && promised-state like という時点で、rxdux は上記の問題に当たるはずなので、それについて書いた。

最終的には、全体に対して処理を直列にするのではなく、局所的に処理を直列するという似たような結論に至ったようだ。

直列化した場合、非同期処理の利点が失われてしまう。非同期に処理している間は別のことをできるならともかく redux のような single store だとそれができなくなってしまうので、致命的だろう。

promised-state や rxdux の話はともかく、front end での data store のありかたを考えている。まず大きく online と offline で難易度が大きく変わる。常時 online なら back end に任せるのは手だ。昔ながらの transaction で対応できる。それでも速度的な問題から lock 単位などで慎重になるだろう。 offline を許容するなら transaction 的な問題を含めて data の整合性を維持しなければならない。複数 client 間の競合もあり得る。

promised-state のような直列化の話を見ていると RDBMS の transaction が思い浮かぶ。あれは isolation level を持っている。速度的な問題を解決するためだ。 RDBMS には lock の単位の話もある。 table-level lock / row-level lock 。offline における競合は分散環境の問題だ。git における conflict も似たようなものだろう。

要件次第ではあるけど、難しい問題だ。