NativeScript / worker-loader

36 stars 20 forks source link

Update example to NativeScript 8 #72

Open fpaaske opened 3 years ago

fpaaske commented 3 years ago

It would be very useful to have a working example for NativeScript 8 and Webpack 5.

NathanaelA commented 3 years ago

Or am I missing something?

fpaaske commented 3 years ago

Maybe it's me that's missing something, but I don't think there are created any workers when creating a new project from CLI?

I mean, after doing your intial steps

An example of this would be very useful. Also, for Angular show how to include Angular compiler to allow code sharing in the worker.

NathanaelA commented 3 years ago

Sorry, I totally misread where this comment was at, I was thinking it was in the main Nativescript group. Yeah, this is a good suggestion, btw to directly answer your question it is like the web:

const myWorker = new Worker("~/path/workerfile");

Docs are located here: https://docs.nativescript.org/advanced-concepts.html#multithreading-workers

edusperoni commented 3 years ago

worker-loader is no longer needed with NS 8 and webpack 5. You can use the same as the web API (which @NathanaelA exemplified).

fpaaske commented 3 years ago

Thank you very much!

lospringliu commented 3 years ago

can anybody provide the typescript example? it complains self not found

lospringliu commented 3 years ago

the type declaration is no longer needed, but HOT seems to have problem

resolving "./app.scss" to "./app.ios.scss"
Error: ENAMETOOLONG: name too long, open '/Users/xcliu/Documents/swtcore8t/platforms/ios/swtcore8t/app/_node_modules_ts-loader_index_js_ruleSet_1_rules_1_use_0_node_modules_nativescript_webpack_dist_loaders_nativescript-worker-loader_index_js_node_modules_nativescript_webpack_dist_loaders_nativescript-hot-loader_index_js_ruleSet_1_rules_7_use_0_app_wallet_wallet-view-model_ts_32_23-71.7a2ffae20a562668f3d8.hot-update.json'
manciuszz commented 3 years ago

worker-loader is no longer needed with NS 8 and webpack 5. You can use the same as the web API (which @NathanaelA exemplified).

... but what about webpack "export/import" and hot reloading functionality of workers? Isn't that the whole point why we used worker-loader in the first place?

const myWorker = new Worker("~/path/workerfile");

If we don't use the 'worker-loader', we have to configure webpack manually to COPY worker files near ./assets folder. The worker-loader generated that for us on the fly AND bootstrapped for webpack use.

-- EDIT:

I dug deeper into the webpack configs and noticed that "nativescript-worker-loader" is actually configured inside the base config. However, this made me cry xD

const NEW_WORKER_WITH_STRING_RE = /new\s+Worker\((['"`].+['"`])\)/;
/**
 * Replaces
 * new Worker('./somePath')
 * with
 * new Worker(new URL('./somePath', import.meta.url))
 */
function loader(content, map) {
    const source = content.replace(NEW_WORKER_WITH_STRING_RE, 'new Worker(new URL($1, import.meta.url))');
    this.callback(null, source, map);
}

This means no "new Worker(variable_to_worker_path)" for us and also how does it parse template strings?

Even taking the working example of const myWorker = new Worker("~/path/workerfile");

What about multiple workers?

const myWorker1 = new Worker("~/path/workerfile1");
const myWorker2 = new Worker("~/path/workerfile2");
const myWorker3 = new Worker("~/path/workerfile3");
const NEW_WORKER_WITH_STRING_RE = /new\s+Worker\((['"`].+['"`])\)/; // will replace only one case and ignore the rest inside the source code file.

// Solution: 
const NEW_WORKER_WITH_STRING_RE = /new\s+Worker\((['"`].+['"`])\)/g; // Add the regex global option.

--EDIT:

By the way, I'm getting ReferenceError: URL is not defined too, because it fails to evaluate the variables inside the template string.

const workerFile = "test.worker.js";
const jsWorker = new Worker(`~/workers/${workerFile}`);

Temporary fix would be to use a path map like

const SelectedWorker = ({
    "fix": () => new Worker("~/workers/fix.worker.js"),
    "the": () => new Worker("~/workers/the.worker.js"),
    "worker": () => new Worker("~/workers/worker.worker.js"),
    "loader": () => new Worker("~/workers/loader.worker.js")
})[workerName.replace(new RegExp("#.*"), "")];

--EDIT AGAIN:

... Furthermore this stupid immortal bug mentioned @ https://github.com/NativeScript/android-runtime/issues/1602 still exists on "@nativescript/android": "8.0.0",

Getting spammed with

04-29 03:01:56.360 16997 17844 F libc : ../../buildtools/third_party/libc++abi/trunk/src/abort_message.cpp:72: abort_message: assertion "terminate_handler unexpectedly threw an exception" failed

after worker termination...

Easy to reproduce when your dealing with a lot of open workers, also although it happens on android-runtime@6.4.1 too, it's bearable compared to all other versions and it takes a lot of tries to purposely crash the app via worker open/close.

Fix pls.

lospringliu commented 3 years ago

@NathanaelA , the document still uses plugin but did not provide how to configure plugin for webpack 5

const WorkerScript = require("nativescript-worker-loader!./worker-script.js");
const worker = new WorkerScript();
abhayastudios commented 3 years ago

Same thing, trying to get my plugin to work with NS8

Working on NS6:

let worker;

if (global.TNS_WEBPACK) {
  let Worker = require("nativescript-worker-loader!./get-contacts-worker.js");
  worker = new Worker;
} else {
  worker = new Worker('./get-contacts-worker.js'); // relative for caller script path
}

On NS8 changed to:

const Worker = require("./get-contacts-worker.js");
const worker = new Worker();

Keep on getting:

Worker is not a constructor

A demo app would go a long way :)

manciuszz commented 3 years ago

Same thing, trying to get my plugin to work with NS8

Working on NS6:

let worker;

if (global.TNS_WEBPACK) {
  let Worker = require("nativescript-worker-loader!./get-contacts-worker.js");
  worker = new Worker;
} else {
  worker = new Worker('./get-contacts-worker.js'); // relative for caller script path
}

On NS8 changed to:

const Worker = require("./get-contacts-worker.js");
const worker = new Worker();

Keep on getting:

Worker is not a constructor

A demo app would go a long way :)

You have to use a normal string in order for the workers in NS8 to work. For ex.:

const jsWorker = new Worker("~/get-contacts-worker.js"); // '~' stands for root

"nativescript-worker-loader!./get-contacts-worker.js" the prefix is no longer needed.

Take note that currently you can't use variables. For ex.: "new Worker(path_to_worker_variable)". Furthermore, you can't have more than one worker being initialized in the same file, because the REGEX doesn't support that. You could use hooks to automatically inject a fixed 'nativescript-worker-loader' based on the solution mentioned above.

If you take a look at the nativescript-worker-loader inside NS8, you'll notice that during webpack bundling process, your worker initialization source code gets replaced from new Worker("~/get-contacts-worker.js") to new Worker(new URL("~/get-contacts-worker.js", import.meta.url)).

For workarounds, refer to the solutions mentioned in comments above.

Anyways, there's a really high chance that your application will crash due to an old bug concerning worker termination.

Personally, at the current state of affairs, I suggest to make a Worker wrapper that works on the main thread, so you don't need to rewrite the whole worker code and just replace one word in the future once workers get properly fixed.

abhayastudios commented 3 years ago

@manciuszz thanks for the reply!

When I use your suggestion above it is complaining:

JS: ERROR Error: Uncaught (in promise): Error: com.tns.NativeScriptException: Failed to find module: “~/get-contacts-worker.js”, relative to: /app/
JS: (file: src/webpack:/@nativescript/template-hello-world-ng/node_modules/nativescript-contacts-lite/index.js:7:0)

I assume that what is happening (considering the error “relative to /app/“) that it is looking in my app root, but really the const worker is code from a plugin that I wrote, so the worker resides in the src of the plugin and not in the app itself.

Any idea?

Thanks!

manciuszz commented 3 years ago

@manciuszz thanks for the reply!

When I use your suggestion above it is complaining:

JS: ERROR Error: Uncaught (in promise): Error: com.tns.NativeScriptException: Failed to find module: “~/get-contacts-worker.js”, relative to: /app/
JS: (file: src/webpack:/@nativescript/template-hello-world-ng/node_modules/nativescript-contacts-lite/index.js:7:0)

I assume that what is happening (considering the error “relative to /app/“) that it is looking in my app root, but really the const worker is code from a plugin that I wrote, so the worker resides in the src of the plugin and not in the app itself.

Any idea?

Thanks!

If I remember correctly "~/*" points to ["app/*"],. So your assumption is right. The fix would be to point the path correctly. Not sure, if "./get-contacts-worker.js" would work. It's probably "../node_modules/nativescript-contacts-lite/src/get-contacts-worker.js" or something like that.

nikoTM commented 3 years ago

I am facing the same issue. Was not able to get it working, seems like worker path never resolves correctly if it's called from within the plugin. As a workaround I've copied the worker to my codebase and I am passing it up from there like this:

const worker = isAndroid ?  new Worker('../libs/android-worker.js') : null;
const download = new DownloadProgress(worker);
alexander-mai commented 3 years ago

I can't get workers running with angular. The worker content is not contained in the compiled source folder.

I created a new project with the blank-ng template and added a worker on /app/workers/useless.worker.ts I imported it with this.worker = new Worker("~/workers/useless.worker"); in the cunstructor of the home.component.ts.

On compile time I get the following error:

ERROR in ./src/app/home/home.component.ts 5:22-86
Module not found: Error: Can't resolve '~/workers/useless.worker' in '/home/ns-projects/ns8-angular-worker-demo/src/app/home'
resolve '~/workers/useless.worker' in '/home/ns-projects/ns8-angular-worker-demo/src/app/home'
  Parsed request is a module
  using description file: /home/ns-projects/ns8-angular-worker-demo/package.json (relative path: ./src/app/home)
    using description file: /home/ns-projects/ns8-angular-worker-demo/package.json (relative path: ./src/app/home)
      using description file: /home/ns-projects/ns8-angular-worker-demo/package.json (relative path: ./src/workers/useless.worker)
no extension
          /home/ns-projects/ns8-angular-worker-demo/src/workers/useless.worker doesn't exist
        .ios.ts
          /home/ns-projects/github/ns8-angular-worker-demo/src/workers/useless.worker.ios.ts doesn't exist
        .ts
          /home/ns-projects/ns8-angular-worker-demo/src/workers/useless.worker.ts doesn't exist
        .ios.js
          /home/ns-projects/ns8-angular-worker-demo/src/workers/useless.worker.ios.js doesn't exist
        .js
          /home/ns-projects/github/ns8-angular-worker-demo/src/workers/useless.worker.js doesn't exist
        .ios.css
          /home/ns-projects/ns8-angular-worker-demo/src/workers/useless.worker.ios.css doesn't exist
        .css
         /home/ns-projects/ns8-angular-worker-demo/src/workers/useless.worker.css doesn't exist
        .ios.scss
          /home/ns-projects/ns8-angular-worker-demo/src/workers/useless.worker.ios.scss doesn't exist
        .scss
          /home/ns-projects/ns8-angular-worker-demo/src/workers/useless.worker.scss doesn't exist
        .ios.json
         /home/ns-projects/ns8-angular-worker-demo/src/workers/useless.worker.ios.json doesn't exist
        .json
         /home/ns-projects/ns8-angular-worker-demo/src/workers/useless.worker.json doesn't exist
        as directory
          /home/ns-projects/ns8-angular-worker-demo/src/workers/useless.worker doesn't exist
      root path /home/ns-projects/ns8-angular-worker-demo
        using description file: /home/ns-projects/ns8-angular-worker-demo/package.json (relative path: ./home/ns-projects/ns8-angular-worker-demo/src/workers/useless.worker)
          no extension
            /home/ns-projects/ns8-angular-worker-demo/home/ns-projects/ns8-angular-worker-demo/src/workers/useless.worker doesn't exist
          .ios.ts
            /home/ns-projects/ns8-angular-worker-demo/home/ns-projects/ns8-angular-worker-demo/src/workers/useless.worker.ios.ts doesn't exist
          .ts
            /home/ns-projects/ns8-angular-worker-demo/home/ns-projects/ns8-angular-worker-demo/src/workers/useless.worker.ts doesn't exist
          .ios.js
            /home/ns-projects/ns8-angular-worker-demo/home/ns-projects/ns8-angular-worker-demo/src/workers/useless.worker.ios.js doesn't exist
          .js
            /home/ns-projects/ns8-angular-worker-demo/home/ns-projects/ns8-angular-worker-demo/src/workers/useless.worker.js doesn't exist
          .ios.css
            /home/ns-projects/ns8-angular-worker-demo/home/ns-projects/ns8-angular-worker-demo/src/workers/useless.worker.ios.css doesn't exist
          .css
            /home/ns-projects/ns8-angular-worker-demo/home/ns-projects/ns8-angular-worker-demo/src/workers/useless.worker.css doesn't exist
          .ios.scss
            /home/ns-projects/ns8-angular-worker-demo/home/ns-projects/ns8-angular-worker-demo/src/workers/useless.worker.ios.scss doesn't exist
          .scss
            //home/ns-projects/ns8-angular-worker-demo/home/ns-projects/ns8-angular-worker-demo/src/workers/useless.worker.scss doesn't exist
          .ios.json
            /home/ns-projects/ns8-angular-worker-demo/home/ns-projects/ns8-angular-worker-demo/src/workers/useless.worker.ios.json doesn't exist
          .json
            /home/ns-projects/ns8-angular-worker-demo/home/ns-projects/ns8-angular-worker-demo/src/workers/useless.worker.json doesn't exist
          as directory
            /home/ns-projects/ns8-angular-worker-demo/home/ns-projects/ns8-angular-worker-demo/src/workers/useless.worker doesn't exist
    aliased with mapping '~': '/home/ns-projects/ns8-angular-worker-demo/src' to '/home/ns-projects/ns8-angular-worker-demo/src/workers/useless.worker'
      using description file: /home/ns-projects/ns8-angular-worker-demo/package.json (relative path: ./src/app/home)
        using description file: /home/ns-projects/ns8-angular-worker-demo/package.json (relative path: ./src/workers/useless.worker)
          no extension
            /home/ns-projects/ns8-angular-worker-demo/src/workers/useless.worker doesn't exist
          .ios.ts
            /home/ns-projects/ns8-angular-worker-demo/src/workers/useless.worker.ios.ts doesn't exist
          .ts
            /home/ns-projects/ns8-angular-worker-demo/src/workers/useless.worker.ts doesn't exist
          .ios.js
            /home/ns-projects/ns8-angular-worker-demo/src/workers/useless.worker.ios.js doesn't exist
          .js
            /home/ns-projects/ns8-angular-worker-demo/src/workers/useless.worker.js doesn't exist
          .ios.css
            /home/ns-projects/ns8-angular-worker-demo/src/workers/useless.worker.ios.css doesn't exist
          .css
            /home/ns-projects/ns8-angular-worker-demo/src/workers/useless.worker.css doesn't exist
          .ios.scss
            /home/ns-projects/ns8-angular-worker-demo/src/workers/useless.worker.ios.scss doesn't exist
          .scss
            /home/ns-projects/ns8-angular-worker-demo/src/workers/useless.worker.scss doesn't exist
          .ios.json
            /home/ns-projects/ns8-angular-worker-demo/src/workers/useless.worker.ios.json doesn't exist
          .json
            /home/ns-projects/ns8-angular-worker-demo/src/workers/useless.worker.json doesn't exist
          as directory
            /home/ns-projects/ns8-angular-worker-demo/src/workers/useless.worker doesn't exist
        root path /home/ns-projects/ns8-angular-worker-demo
          using description file: /home/ns-projects/ns8-angular-worker-demo/package.json (relative path: ./home/ns-projects/ns8-angular-worker-demo/src/workers/useless.worker)
            no extension
              /home/ns-projects/ns8-angular-worker-demo/home/ns-projects/ns8-angular-worker-demo/src/workers/useless.worker doesn't exist
            .ios.ts
              /home/ns-projects/ns8-angular-worker-demo/home/ns-projects/ns8-angular-worker-demo/src/workers/useless.worker.ios.ts doesn't exist
            .ts
              /home/ns-projects/ns8-angular-worker-demo/home/ns-projects/ns8-angular-worker-demo/src/workers/useless.worker.ts doesn't exist
            .ios.js
              /home/ns-projects/ns8-angular-worker-demo/home/ns-projects/ns8-angular-worker-demo/src/workers/useless.worker.ios.js doesn't exist
            .js
              /home/ns-projects/ns8-angular-worker-demo/home/ns-projects/ns8-angular-worker-demo/src/workers/useless.worker.js doesn't exist
            .ios.css
              /home/ns-projects/ns8-angular-worker-demo/home/ns-projects/ns8-angular-worker-demo/src/workers/useless.worker.ios.css doesn't exist
            .css
              /home/ns-projects/ns8-angular-worker-demo/home/ns-projects/ns8-angular-worker-demo/src/workers/useless.worker.css doesn't exist
            .ios.scss
              /home/ns-projects/ns8-angular-worker-demo/home/ns-projects/ns8-angular-worker-demo/src/workers/useless.worker.ios.scss doesn't exist
            .scss
              /home/ns-projects/ns8-angular-worker-demo/home/ns-projects/ns8-angular-worker-demo/src/workers/useless.worker.scss doesn't exist
            .ios.json
              /home/ns-projects/ns8-angular-worker-demo/home/ns-projects/ns8-angular-worker-demo/src/workers/useless.worker.ios.json doesn't exist
            .json
              /home/ns-projects/ns8-angular-worker-demo/home/ns-projects/ns8-angular-worker-demo/src/workers/useless.worker.json doesn't exist
            as directory
              /home/ns-projects/ns8-angular-worker-demo/home/ns-projects/ns8-angular-worker-demo/src/workers/useless.worker doesn't exist
 @ ./src/app/home/home-routing.module.ts 2:0-49 5:39-52
 @ ./src/app/home/home.module.ts 2:0-58 8:91-108
 @ ./src/app/app-routing.module.ts 8:28-60
 @ ./src/app/app.module.ts 2:0-56 9:84-100
 @ ./src/main.ts 4:0-45 6:69-78

Does I need something to add on webpack.config.js? The documentation on https://docs.nativescript.org/advanced-concepts.html#multithreading-workers is ambiguous and not showing some special handling for angular like discribed in the worker-loader-plugin. The implementation shown on sample usage new Worker('path/to/worker' differs from main-view-model.js const WorkerScript = require("nativescript-worker-loader!./worker-script.js"); const worker = new WorkerScript(); In this thread @edusperoni said that worker-loader is not needed anymore but but the documentation is referencing the worker-loader plugin.

Could the documentation on the plugin and/or the nativescript page updated?

I created an example project: https://github.com/alexander-mai/ns8-angular-worker-demo

alexander-mai commented 3 years ago

Could you please help on this issue @NathanaelA?

I think the Angular Demo is not up to date for Nativescript 8 with webpack 5.

jr-dimedis commented 2 years ago

I like to give a big +1 on this topic!

It took me hours to find out, that using workers is really dead simple if you know how to do it and if the documentation would be accurate for NS8.

I am using TypeScript and at the end I this was working for me:

// app/home-page/home-page.ts
// --------------------------

// Load worker.
// IMPORTANT: Don't append any extension to the filename!!!
const worker = new Worker("./worker");
worker.onerror = (err) => { ... };
worker.onmessage = (msg) => { ... };
worker.postMessage({ some: "thing" });

// app/home-page/worker.ts
// -----------------------

require("globals");

global.onmessage = (msg) => {
  // handle msg.data.some
  global.postMessage({...});
};

I went the whole journey through webpack configuration, nativescript-worker-loader plugin installation & configuration, modules not loading, "is not a constructor" error messages and so on. All this was not necessary but mentioned in the docs and you find many misleading & outdated issues and Stackoverflow posts.

And finally a major issue was just passing a relative path to the worker script omitting the file extension!

So big thinks to the team that using Workers is much simpler compared to previous versions of the framework. :)

But please update the documentation so everyone can make use of this great feature without any hassles.

soscler commented 2 years ago

Late here, but this could save you time in setting up worker in {NS8}.

  1. Not necessary, but name your worker file as *.worker.service.ts (e.g.: image.worker.service.ts)
  2. Still not necessary, but put all your workers files in a folder called workers
  3. Add these in tsconfig.json
    {
    ...,
    "lib": [..., "Webworker"],
    "include": [..., "src/**/*.worker.service.ts"],
    ...
    }
    • Adding webworkerin libwill make {NS} use webworker API instead of browser API.
    • Including .worker.service.ts will make TS compiler to make sure to include your `.worker.service.ts files`.

NB: naming is not conventional. In case you decice to name your worker files differently, make sure to edit include section accordingly.