0

I have a mobx store (admisionStore) which contain actions and observables, some of the actions load data from the API using Axios, but it is a long running task that slow the UI, I read about web worker and they may be helpful in this scenario.

I follwed the guidelines in this article: https://dev.to/nicolasrannou/web-workers-in-create-react-app-cra-without-unmounting-4865 and this is what I have:

//typings-custom/custom.d.ts

declare module 'worker-loader!*' {
  class WebpackWorker extends Worker {
    constructor();
  }

  export default WebpackWorker;
}
//worker.ts

import {expose} from 'comlink';
export interface WorkerType {
  busy(value: number): number;
}

const obj: WorkerType = {
  busy(value: number) {
    let j = 0;
    const start = Date.now();

    for (let i = 0; i < value; i++) {
      //
      j++;
      const end = Date.now();
      // if it has been busy for more than 10s return
      if (end - start > 10000) {
        return i;
      }
    }
    return j;
  },
};

expose(obj);
//App.tsx

import React from 'react';
import {wrap} from 'comlink';
/* eslint-disable import/no-webpack-loader-syntax */
import Worker from 'worker-loader!./worker';
import { WorkerType } from './worker';

const handle = async () => {
startDummyWorker()
};

async function startDummyWorker() {
  const worker = new Worker();
  const obj = wrap<WorkerType>(worker);
  const finished = await obj.busy(10000000000000000000);
  console.log(finished);
}

const App = () => {
  return (
    <div>
      <button onClick={handle}>Press</button>
      <button onClick={() => console.log('HOLA')}>press2</button>
    </div>
  );
};

export default App;

//webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.worker\.(js|ts)$/i,
        use: [
          {
            loader: 'worker-loader',
          },
        ],
      },
    ],
  },
};

As you can see I had to create a type declaration file for typescript as well as an interface WorkerType otherwise typescript will complain, I also created a webpack.config.js file in order to cofigure worker-loader: https://webpack.js.org/loaders/worker-loader/#config

Everything works fine, but when I tried to implement this approach in React, using mobx as my state management, it didn't work. This is my code:

//worker.ts

import { expose } from 'comlink';
import { useContext } from 'react';
import { RootStoreContext } from '../app/stores/rootStore';
export interface WorkerType {
  load(): void;
}

const rootStore = useContext(RootStoreContext);
const { loadAsegurado } = rootStore.admisionStore;

const admisionWorker: WorkerType = {
  load() {
    loadAsegurado; //mobx action loadAsegurado: () => Promise<void>
  },
};
expose(admisionWorker);
//AdmisionDashboard.tsx
import React, { useContext, useEffect} from 'react';
import { observer } from 'mobx-react-lite';
import { RootStoreContext } from '../../../../app/stores/rootStore';
import { wrap } from 'comlink';
/* eslint-disable import/no-webpack-loader-syntax */
import Worker from 'worker-loader!../../../../my-first-worker/worker';
import { WorkerType } from '../../../../my-first-worker/worker';

function startAdmisionWorker() {
  const worker = new Worker();
  const workerApi = wrap<WorkerType>(worker);
  workerApi.load();
}

const AdmisionDashboard=()=>{
const rootStore = useContext(RootStoreContext);
const {loadAsegurado, setOperacion} = rootStore.admisionStore
const { token } = rootStore.commonStore;

useEffect(() => {
    if (token === null) {
      history.push('/');
    } else {
      setOperacion('buscar');
      startAdmisionWorker();//Here I start my web worker
    }
  }, [setOperacion, token]);

[...] // rest of the code
}
export default observer(AdmisionDashboard);

When I start the application I get this log:

Microsoft Windows [Version 6.3.9600]
(c) 2013 Microsoft Corporation. All rights reserved.

C:\Users\Desarrollo\Desktop\NHME-app>npm start

> admision-app@0.1.0 start C:\Users\Desarrollo\Desktop\NHME-app
> react-scripts start

Starting the development server...


<--- Last few GCs --->

[12160:0000007516C6E060]   265687 ms: Scavenge 2006.3 (2067.4) -> 1992.7 (2068.2) MB, 38.8 / 0.1 ms  (average mu = 0.260, current mu = 0.315) allocation failure
[12160:0000007516C6E060]   268205 ms: Mark-sweep 2010.8 (2071.5) -> 1993.4 (2068.1) MB, 2315.7 / 0.4 ms  (average mu = 0.224, current mu = 0.184) allocation failure scavenge might not succeed


<--- JS stacktrace --->

==== JS stack trace =========================================

    0: ExitFrame [pc: 00007FF69AC6463D]
Security context: 0x02baba3008a1 <JSObject>
    1: /* anonymous */ [000002B6DF429F31] [C:\Users\Desarrollo\Desktop\NHME-app\node_modules\webpack\node_modules\acorn\dist\acorn.js:~2945] [pc=0000017352FE77BB](this=0x0282de28a389 <Parser map = 0000010C5D327E41>)
    2: /* anonymous */ [000002B6DF428BB1] [C:\Users\Desarrollo\Desktop\NHME-app\node_modules\webpack\node_modules\acorn\dist\acorn.js:1155] [b...

FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory

Writing Node.js report to file: report.20200828.085936.12160.0.001.json
Node.js report completed
 1: 00007FF69A09232F napi_wrap+124543
 2: 00007FF69A0336A6 public: bool __cdecl v8::base::CPU::has_sse(void)const __ptr64+34502
 3: 00007FF69A034366 public: bool __cdecl v8::base::CPU::has_sse(void)const __ptr64+37766
 4: 00007FF69A838C5E private: void __cdecl v8::Isolate::ReportExternalAllocationLimitReached(void) __ptr64+94
 5: 00007FF69A820CA1 public: class v8::SharedArrayBuffer::Contents __cdecl v8::SharedArrayBuffer::Externalize(void) __ptr64+833
 6: 00007FF69A6EE56C public: static void __cdecl v8::internal::Heap::EphemeronKeyWriteBarrierFromCode(unsigned __int64,unsigned __int64,class v8::internal::Isolate * __ptr64)+1436
 7: 00007FF69A6F9910 public: void __cdecl v8::internal::Heap::ProtectUnprotectedMemoryChunks(void) __ptr64+1312
 8: 00007FF69A6F6444 public: static bool __cdecl v8::internal::Heap::PageFlagsAreConsistent(class v8::internal::HeapObject)+3204
 9: 00007FF69A6EBCD3 public: bool __cdecl v8::internal::Heap::CollectGarbage(enum v8::internal::AllocationSpace,enum v8::internal::GarbageCollectionReason,enum v8::GCCallbackFlags) __ptr64+1283
10: 00007FF69A6EA4A4 public: void __cdecl v8::internal::Heap::AddRetainedMap(class v8::internal::Handle<class v8::internal::Map>) __ptr64+2356
11: 00007FF69A70B775 public: class v8::internal::Handle<class v8::internal::HeapObject> __cdecl v8::internal::Factory::NewFillerObject(int,bool,enum v8::internal::AllocationType) __ptr64+53
12: 00007FF69A477A49 ??4iterator@JumpTableTargetOffsets@interpreter@internal@v8@@QEAAAEAV01234@$$QEAV01234@@Z+4057
13: 00007FF69AC6463D public: virtual bool __cdecl v8::internal::SetupIsolateDelegate::SetupHeap(class v8::internal::Heap * __ptr64) __ptr64+567949
14: 0000017352FE77BB
npm ERR! code ELIFECYCLE
npm ERR! errno 134
npm ERR! admision-app@0.1.0 start: `react-scripts start`
npm ERR! Exit status 134
npm ERR!
npm ERR! Failed at the admision-app@0.1.0 start script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR!     C:\Users\Desarrollo\AppData\Roaming\npm-cache\_logs\2020-08-28T14_59_37_653Z-debug.log

C:\Users\Desarrollo\Desktop\NHME-app>

Any suggestion would be greatly appreciated. Thanks in advance.

Orpheus
  • 1
  • 2
  • Not an answer to your question, and maybe I'm misunderstanding you: I'd say you don't need a webworker just to get data using Axios. If you just do a axios.get(...).then(result => {....}) it should not block your interface. Of course if you put 'await' in front of it, then it will block before continuing, but that's a feature! – Simon Groenewolt Sep 01 '20 at 07:57
  • @SimonGroenewolt thanks for your comment. All my http request are async, you may think that the slow performance comes from waiting a response from the server, but that is not the case. What I am trying to achieve is to improve the UI performance by loading data in another thread while the user manipulates the app without "feeling" a poor experience. The data that comes from the server is paginated, so there're hundreds of thousands of data on the UI, That's why I thought web worker may help. – Orpheus Sep 07 '20 at 15:35

0 Answers0