首页 > 解决方案 > 如何序列化/反序列化 JS observable?

问题描述

如果我有一个可观察的流,使用RxJS或类似的,像这样(大理石图):

----1------2------3--4---|

如何将其序列化为NDJSON之类的格式,以便将其保存在数据库中?保存后,如何将其重构为可观察对象?

对于上下文,我想在 DOM 中捕获用户操作流(如鼠标拖动等),然后在稍后与用户最初执行的操作完全相同的时间重播它们。

标签: javascriptrxjsobservable

解决方案


如果时间很重要,您可能希望将时间值与事件本身一起存储。

小POC:

import { fromEvent, Observable, of, timer } from "rxjs";
import {
  map,
  tap,
  mergeMap,
  concatMap,
  takeUntil,
  mapTo
} from "rxjs/operators";

let latestCache: Array<{ time: number; event: MouseEvent }>;

fromEvent(document.getElementById("start-cache"), "click")
  .pipe(
    concatMap(() => {

      const source = fromEvent(window, "mousemove").pipe(
        tap((x: MouseEvent) => console.log(x.clientX, x.clientY)),
        takeUntil(timer(1000)),
        store( newCache => (latestCache = newCache))
      );

      function store(
        onCacheCreated: (
          cache: Array<{ time: number; event: MouseEvent }>
        ) => void
      ) {
        const cache = [];
        let lastTime = Date.now();
        return tap({
          next: (x: MouseEvent) => {
            cache.push({ time: Date.now() - lastTime, event: x });
            lastTime = Date.now();
          },
          complete: () => onCacheCreated(cache)
        });
      }

      return source;
    })
  )
  .subscribe();

fromEvent(document.getElementById("replay-cache"), "click")
  .pipe(
    concatMap(() => {
      if (!latestCache) {
        console.error("no cache yet");
      }
      return of(...latestCache).pipe(
        concatMap(x => timer(x.time).pipe(mapTo(x.event))),
        tap((x: MouseEvent) => console.log(x.clientX, x.clientY))
      );
    })
  )
  .subscribe();
<h1>RxJS playground</h1>

<p>Open the console below to see results</p>

<button id="start-cache">Start Cache </button>
<button id="replay-cache">Replay Cache </button>

https://stackblitz.com/edit/rxjs-vxwurq?file=index.ts

从这里开始,您可以使用 onCacheCreated 函数随心所欲地存储数组。


推荐阅读