Multithreading in NativeScript does not work - multithreading

When I try using workers (aka multithreading) in my Angular - NativeScript app the compiled worker file is not found during app execution. I've found a similar issue on GitHub, but the instructions there did not help me.
Running the app results in the following output:
Project successfully built.
Installing on device 4865d3ab...
Successfully installed on device with identifier '4865d3ab'.
Refreshing application on device 4865d3ab...
Successfully synced application org.nativescript.app on device 4865d3ab.
JS: Angular is running in the development mode. Call enableProdMode() to enable the production mode.
JS: Warning: Setting the 'itemWidth' property of 'ListViewGridLayout' is not supported by the Android platform.
JS: Warning: Setting the 'itemHeight' property of 'ListViewGridLayout' is not supported by the Android platform.
JS: Warning: Setting the 'itemHeight' property of 'ListViewGridLayout' is not supported by the Android platform.
JS: Scan!
JS: Subnet: 192.168.2
JS: Permission is not granted (Error: com.tns.NativeScriptException: Failed to find module: "307b720bbe3cb7a8458a.worker.js", relative to: app/tns_modules/
JS: com.tns.Module.resolvePathHelper(Module.java:146)
JS: com.tns.Module.resolvePath(Module.java:55)
JS: com.tns.Runtime.callJSMethodNative(Native Method)
JS: com.tns.Runtime.dispatchCallJSMethodNative(Runtime.java:1160)
JS: com.tns.Runtime.callJSMethodImpl(Runtime.java:1040)
JS: com.tns.Runtime.callJSMethod(Runtime.java:1027)
JS: com.tns.Runtime.callJSMethod(Runtime.java:1007)
JS: com.tns.Runtime.callJSMethod(Runtime.java:999)
JS: com.tns.NativeScriptActivity.onRequestPermissionsResult(NativeScriptActivity.java:58)
JS: android.app.Activity.dispatchRequestPermissionsResult(Activity.java:7630)
JS: android.app.Activity.dispatchActivityResult(Activity.java:7480)
JS: android.app.ActivityThread.deliverResults(ActivityThread.java:4489)
JS: android.app.ActivityThread.handleSendResult(ActivityThread.java:4538)
JS: android.app.servertransaction.ActivityResultItem.execu...
How can I solve this problem?
Many thanks for your help.
EDIT
I am importing the worker with:
import TestWorker from 'worker-loader!./workers/test.worker.js'
The worker itself has the file name test.worker.ts and the following content:
const context: Worker = self as any;
context.onmessage = msg => {
setTimeout(() => {
console.log('Inside TS worker...');
console.log(msg);
(<any>global).postMessage('TS Worker');
}, 500);
};
What am I doing wrong here? Many thanks for the help. Very nice of you.

If you are using the nativescript-worker-loader plugin as Manoj suggested, you will need to import the worker with
import TestWorker from 'nativescript-worker-loader!./workers/test.worker.js'
not with
import TestWorker from 'worker-loader!./workers/test.worker.js'
You can also use
import TestWorker from 'nativescript-worker-loader!./workers/test.worker'
that should not make any difference.
Also make sure that you declare the module of the typings/custom.d.ts file as described in the nativescript-worker-loader readme as nativescript-worker-loader!*

Related

NodeJs Debugging Client-Side Visual Studio Code with FuseJs

I have two application which has developed with Typescript. They are server-side and client-side application. I used NodeJs for them. On client-side also used fuseJs for hot-reload feature. But I have a problem I can't debug the client-side application. I'm always writing to console and there is so much "console.log() lines".
I can debug my server-side. But I can't debug client-side.
Here is my fusejs configuration:
const { FuseBox, WebIndexPlugin, CSSPlugin, CSSResourcePlugin } = require("fuse-box");
const { src } = require("fuse-box/sparky");
const fuse = FuseBox.init({
tsConfig: "tsconfig.json",
homeDir: "src",
target: "browser#es6",
output: "dist/$name.js",
plugins: [
WebIndexPlugin({template:'src/dashboard.html'}),
CSSPlugin()],
});
fuse.dev({port:8080});
fuse
.bundle("app")
.instructions(" > main.ts")
.hmr()
.watch();
fuse.run();
I was run my client-side app like: node fuse.js but I read this docs: https://nodejs.org/api/debugger.html then I added "debugger" line to my code and I run it: node inspect fuse.js
But I can't still debugging it. Somebody suggested chrome debug extension but I don't want change my any config or don't want to be reason for it. Is there any other way before try this extension? Can anybody help me?

How to import a node module inside an angular web worker?

I try to import a node module inside an Angular 8 web worker, but get an compile error 'Cannot find module'. Anyone know how to solve this?
I created a new worker inside my electron project with ng generate web-worker app, like described in the above mentioned ng documentation.
All works fine until i add some import like path or fs-extra e.g.:
/// <reference lib="webworker" />
import * as path from 'path';
addEventListener('message', ({ data }) => {
console.log(path.resolve('/'))
const response = `worker response to ${data}`;
postMessage(response);
});
This import works fine in any other ts component but inside the web worker i get a compile error with this message e.g.
Error: app/app.worker.ts:3:23 - error TS2307: Cannot find module 'path'.
How can i fix this? Maybe i need some additional parameter in the generated tsconfig.worker.json?
To reproduce the error, run:
$ git clone https://github.com/hoefling/stackoverflow-57774039
$ cd stackoverflow-57774039
$ yarn build
Or check out the project's build log on Travis.
Note:
1) I only found this as a similar problem, but the answer handles only custom modules.
2) I tested the same import with a minimal electron seed which uses web workers and it worked, but this example uses plain java script without angular.
1. TypeScript error
As you've noticed the first error is a TypeScript error. Looking at the tsconfig.worker.json I've found that it sets types to an empty array:
{
"compilerOptions": {
"types": [],
// ...
}
// ...
}
Specifying types turns off the automatic inclusion of #types packages. Which is a problem in this case because path has its type definitions in #types/node.
So let's fix that by explicitly adding node to the types array:
{
"compilerOptions": {
"types": [
"node"
],
// ...
}
// ...
}
This fixes the TypeScript error, however trying to build again we're greeted with a very similar error. This time from Webpack directly.
2. Webpack error
ERROR in ./src/app/app.worker.ts (./node_modules/worker-plugin/dist/loader.js!./src/app/app.worker.ts)
Module build failed (from ./node_modules/worker-plugin/dist/loader.js):
ModuleNotFoundError: Module not found: Error: Can't resolve 'path' in './src/app'
To figure this one out we need to dig quite a lot deeper...
Why it works everywhere else
First it's important to understand why importing path works in all the other modules. Webpack has the concept of targets (web, node, etc). Webpack uses this target to decide which default options and plugins to use.
Ordinarily the target of a Angular application using #angular-devkit/build-angular:browser would be web. However in your case, the postinstall:electron script actually patches node_modules to change that:
postinstall.js (parts omitted for brevity)
const f_angular = 'node_modules/#angular-devkit/build-angular/src/angular-cli-files/models/webpack-configs/browser.js';
fs.readFile(f_angular, 'utf8', function (err, data) {
var result = data.replace(/target: "electron-renderer",/g, '');
var result = result.replace(/target: "web",/g, '');
var result = result.replace(/return \{/g, 'return {target: "electron-renderer",');
fs.writeFile(f_angular, result, 'utf8');
});
The target electron-renderer is treated by Webpack similarily to node. Especially interesting for us: It adds the NodeTargetPlugin by default.
What does that plugin do, you wonder? It adds all known built in Node.js modules as externals. When building the application, Webpack will not attempt to bundle externals. Instead they are resolved using require at runtime. This is what makes importing path work, even though it's not installed as a module known to Webpack.
Why it doesn't work for the worker
The worker is compiled separately using the WorkerPlugin. In their documentation they state:
By default, WorkerPlugin doesn't run any of your configured Webpack plugins when bundling worker code - this avoids running things like html-webpack-plugin twice. For cases where it's necessary to apply a plugin to Worker code, use the plugins option.
Looking at the usage of WorkerPlugin deep within #angular-devkit we see the following:
#angular-devkit/src/angular-cli-files/models/webpack-configs/worker.js (simplified)
new WorkerPlugin({
globalObject: false,
plugins: [
getTypescriptWorkerPlugin(wco, workerTsConfigPath)
],
})
As we can see it uses the plugins option, but only for a single plugin which is responsible for the TypeScript compilation. This way the default plugins, configured by Webpack, including NodeTargetPlugin get lost and are not used for the worker.
Solution
To fix this we have to modify the Webpack config. And to do that we'll use #angular-builders/custom-webpack. Go ahead and install that package.
Next, open angular.json and update projects > angular-electron > architect > build:
"build": {
"builder": "#angular-builders/custom-webpack:browser",
"options": {
"customWebpackConfig": {
"path": "./extra-webpack.config.js"
}
// existing options
}
}
Repeat the same for serve.
Now, create extra-webpack.config.js in the same directory as angular.json:
const WorkerPlugin = require('worker-plugin');
const NodeTargetPlugin = require('webpack/lib/node/NodeTargetPlugin');
module.exports = (config, options) => {
let workerPlugin = config.plugins.find(p => p instanceof WorkerPlugin);
if (workerPlugin) {
workerPlugin.options.plugins.push(new NodeTargetPlugin());
}
return config;
};
The file exports a function which will be called by #angular-builders/custom-webpack with the existing Webpack config object. We can then search all plugins for an instance of the WorkerPlugin and patch its options adding the NodeTargetPlugin.

Using dgram module into browser application

I'm building a web application that use a module that require the dgram module of nodejs, I have to run this application in a browser, but when I try to build it with webpack, it return me errors on the dgram module:
ERROR in C:/Users/Sibo//coap/lib/agent.js
Module not found: Error: Cannot resolve module 'dgram' in C:\Users\Sibo\node_modules\coap\lib
# C:/Users/Sibo//coap/lib/agent.js 12:22-38
ERROR in C:/Users/Sibo//coap/lib/server.js
Module not found: Error: Cannot resolve module 'dgram' in C:\Users\Sibo\node_modules\coap\lib
# C:/Users/Sibo//coap/lib/server.js 11:22-38
Do you know a way to solve this problem?
It's not possible to use dgram in browser for normal sites. It is possible however for Chrome Apps using chrome-dgram.
The only solution to error you're getting is adding node configuration to your Webpack config:
node: {
dgram: 'empty'
}
dgram is basically a node.js core module, and hence you wont be able to use this on browser end.
However, there is a browser equivalent verion of drag module available. See if that is useful for you. If useful, try this
resolve: {
alias: {
dgram: "dgram-browserify"
}
}

TypeError: require(...).connect is not a function

node version: v4.4.4
npm version: 3.9.2
ionic version (app): 2.0.0-beta.7
amqplib version: 0.4.1
I am currently trying to develop an app using Ionic 2 framework and I have decided to introduce messaging in my app by using RabbitMQ within this library. Feel free to inspect the code here for any further reference.
First of all, I installed the library manually using npm install https://github.com/squaremo/amqp.node.git because the release version from npm is outdated.
After that, I added the Typescript definitions for the library via typings install dt~amqplib --global --save.
I created a new page for my app called Page2 where the library is imported...
import * as amqp from 'amqplib/callback_api';
[...]
... and used to connect to the server...
[...]
setConnection() {
amqp.connect(this.connectionUrl, (err: any, connection: amqp.Connection) => {
this.connection = connection;
this.connection.createChannel((err: any, channel: amqp.Channel) => {
this.channel = channel;
this.channel.assertExchange(this.exchange, 'topic', { durable: false });
});
});
}
[...]
The problem comes when I try to run it (I have done it using both an emulator and a native device running Android). If I try to hit the Set connection button, I get the following error:
The error is linked to the line sock = require('net').connect(sockopts, onConnect); of connect.js file. Is there any trouble with NodeJS Net module in the library or is it a misconfiguration I made somewhere in my app?, thanks in advance.
There is no nodeJS server in an ionic app that your library can use.

Reference Socket.io fron Node server in Offline App with Require.js

I am trying to use Socket.io hosted by a Node server. I am using Require.js to manage dependencies. My webapp is Offline capable.
When the webapp is offline, and cannot contact the node server, require.js throws an error because it cannot find the socket.io dependency.
GET http://mikemac.local:8000/socket.io/socket.io.js  require.js:33
Uncaught Error: Script error
http://requirejs.org/docs/errors.html#scripterror
In this case I am not using node/io for the majority of the system, just 'bonus' real time notifications. So The app should run without it.
How can I deal with this? I would like a way to detect that it cannot be found and then disable the socket.io functionality until a connection/refresh attempt.
In your configuration setup fallback to other module and in the fallback indicate that it is offline:
requirejs.config({
enforceDefine: true,
paths: {
socketio: [
'http://mikemac.local:8000/socket.io/socket.io',
//If the CDN location fails, load from this location
'lib/socketoffline'
]
}
});
//socketoffline
define({ offline: true});
//Later
require(['socketio'], function (socketio) {
if (socketio.offline){
// your library did not load:
} else {
// socketio loaded
}
});

Resources