Search code examples
javascripttypescriptweb-worker

Keeping nested prototype methods from object transfering from WebWorker


I need to reserialize an object from a WebWorker sharing the same definitions.

Upon reception of the message, I'm losing all the prototype functions.

Doing

worker.onmessage = ({ data }) => {
    this.programParser = Object.assign(new ProgramModel(), data);
}

Works only for first level prototype functions, but I need a solution for all the nested classes (this object has a large inheritance and dependency tree).

What could I do to do that ?

Current data model looks like :

enter image description here

(note the Object prototypes)

Using flatted and custom revive function (I added the class name in each object from the serializing function), I can achieve a much closer model to what I would need, but some nested references aren't treated as original class objects.

enter image description here

The flatted revive function is the following :

const model = parse(data, (key, val) => {
  if (val !== null && val.className && val.className in models) {
    if (val.className === "DataProvider") {
       console.log(val)
       return new DataProvider(val.providerData)
    }
    return Object.assign(new (<any>models)[val.className](), val)
   }
   return val;
 })

The flatted library is used to keep clear of circular issues of JSON serialization : https://github.com/WebReflection/flatted

Minimal exemple of the issue : https://codesandbox.io/s/zealous-https-n314w?file=/src/index.ts (Object is lost after 2nd level referencing)


Solution

  • I don't want to take any credit from this previous answer, but while the explanation/process through is nice and sound, I believe the proposed solution is not the best one, and btw, I am the author of the flatted library that actually explained, and helped out, in this issue filed against such library, without even knowing there was a discussion here ... sorry I am late ...

    The missing piece of the previous answer is that the same instance gets updated over and over while reviving the whole structure, but nothing like that is actually needed, because either Set or WeakSet can help speeding up the process, avoiding upgrading what's been upgraded already, over and over.

    const {setPrototypeOf} = Reflect;
    const upgraded = new Set;
    const ret = parse(str, (_, v) => {
      if (v && v.className && models[v.className] && !upgraded.has(v)) {
        upgraded.add(v);
        setPrototypeOf(v, models[v.className].prototype);
      }
      return v;
    });
    

    This change doesn't strictly improve the reason it either works or it is the best solution, compared to substitution, but it takes into account performance and redundant upgrades, 'cause unnecessary setPrototypeOf calls, might not be desired at all 😉