简体   繁体   中英

How to serialize/deserialize a JS observable?

If I have an observable stream, using RxJS or similar, like this ( marble diagram):

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

How can I serialize that to a format like NDJSON so I can save it in a database? Once it's saved, how can I reconstitute it as an observable?

For context, I want to capture a stream of user actions in the DOM (like mouse drags, etc.) and then replay them at a later time exactly the same as the user initially performed it.

If time is of the importance, you might want to store the time value along with the events themselves.

Little 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

From here on, you can store the array however you want, using the onCacheCreated function.

I am not sure the exact input and desired result, but one way to do it would be to create an array of observables, and push each observable from the stream to the array of observable.

const observableList = [];
observableList.push(userAction$);

From there, you can subscribe to the observable and serialise it before writing it to your database.

forkJoin(observableList).subscribe(res => {
  // console.log(res);
  // save list on database
})

When you want to read it again, you can probably convert it to an observable using the RxJS from operator, which will allow you to emit the observables in the exact sequence when you subscribe to it.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM