Code Monkey home page Code Monkey logo

workly's Introduction

Workly ๐Ÿ‹๏ธโ€โ™€๏ธโ†’ ๐Ÿ˜„

  • A really simple way to move a stand-alone function/class to a worker thread.
  • Or, expose an object or function in a worker to the main thread.
  • All calls are made asynchronous. Works great with async/await.
  • Only 1kB gzipped.

Install

Download the latest from dist folder

or from npm:

npm install --save workly

Usage

Moving a function to a worker is really simple.

function busyAdd(a, b) {
  let st = Date.now();
  while (true) {
    if ((Date.now() - st) > 2000) break;
  }
  return a + b;
}

(async () => {
  let workerAdd = workly.proxy(busyAdd);
  console.log(await workerAdd(23, 16)); // 39
  // the busyAdd is executed in a worker so
  // the UI does not get blocked
})();

Or, in fact a Class

class Adder {
  constructor() {
    this.count = 0;
  }
  add(a, b) {
    this.count++;
    return a + b;
  }
}

(async () => {
  let WAdder = workly.proxy(Adder);
  let a = await new WAdder(); // instance created/running in worker
  console.log(await a.count); // 0
  console.log(await a.add(23, 16)); // 39
  console.log(await a.count); // 1
})();

Custom workers

The above examples only work when the class/function is not dependent on the containing scope, i.e. other libraries or global objects. But, you can create a custom worker.js file and move the code in there. In the worker, you can expose your object/function/class using workly.expose method.

In this example, the function depends on moment.js

worker.js

importScripts('https://cdn.jsdelivr.net/npm/[email protected]/moment.min.js', '../dist/workly.js');
function friendlyTime(value) {
  return moment(value).calendar(null, {
    sameDay: function (now) {
      if (now - this < 1000 * 60) {
        return "[Just now]";
      } else if (now - this < 1000 * 60 * 60) {
        return "[" + Math.round((now - this) / (1000 * 60)) + " mins ago]";
      } else {
        return '[Today at] LT'
      }
    }
  });
}
workly.expose(friendlyTime);

main.js

(async () => {
  let w = workly.proxy("./worker.js");
  let now = Date.now();
  console.log(now);
  console.log(await w(now));
  console.log(await w(now - (24 * 60 * 60 * 1000)));
  console.log(await w(now - (4 * 24 * 60 * 60 * 1000)));
})();

Caveats

  • If you're not using a custom worker, the function/class being pushed to the worker cannot depend on the containing scope.
  • Since workers do not have access to DOM, DOM manipulation is not supported.
  • Objects passed into functions are not passed by reference, so if the function in the worker updates the passed in object, it will not affect the object in the main scope.

Examples

See the examples folder

License

MIT License (c) Preet Shihn

You may also be interested in

windtalk - Simplest way to communicate between windows or iframes. Work with objects/functions defined in another window or iframe.

workly's People

Contributors

finesse avatar pshihn avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

workly's Issues

How to send progress of worker?

I run a task in worker, and it should send out the progress / rate of the task.

There seems to be no way to post messages in the code of worker, or terminal the worker.

  • postMessage in worker code
  • terminal worker outside

how to determine if workers are running

how to determine if workers are running, lets say if I click an event to spin off a worker, what is the best approach to see if the previous workers are done processing before I begin the new worker?

ReferenceError: Blob is not defined

When I try to run this code it returns the error below. I'm using node v14.5.0.
code:

const workly = require('workly');

function busyAdd(a, b) {
    let st = Date.now();
    while (true) {
        if ((Date.now() - st) > 2000) break;
    }
    return a + b;
}

(async() => {
    let workerAdd = workly.proxy(busyAdd);
    console.log(await workerAdd(23, 16)); // 39
    // the busyAdd is executed in a worker so
    // the UI does not get blocked
})();

error:

(node:33541) UnhandledPromiseRejectionWarning: ReferenceError: Blob is not defined
    at Object.t.proxy (/Users/maxcampbell/Desktop/Web Dev/gpu.js/node_modules/workly/dist/workly.umd.js:1:2011)
    at /Users/maxcampbell/Desktop/Web Dev/gpu.js/test.js:12:28
    at Object.<anonymous> (/Users/maxcampbell/Desktop/Web Dev/gpu.js/test.js:16:3)
    at Module._compile (internal/modules/cjs/loader.js:1201:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1221:10)
    at Module.load (internal/modules/cjs/loader.js:1050:32)
    at Function.Module._load (internal/modules/cjs/loader.js:938:14)
    at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:71:12)
    at internal/main/run_main_module.js:17:47
(Use `node --trace-warnings ...` to show where the warning was created)
(node:33541) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 1)
(node:33541) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

Works perfectly with TypeScript

I tried to make it as clear as possible in the title that this is not exactly an "issue" :-)

I've been using workly in a TS project and it works like a charm.

I thought I'd share here my experience, in case it helps. I think it might be useful also to add some of the following instructions to the README.

Since deferring to workly a pure TS class did not work (as expected), my approach was to build the classes I needed as a UMD with WebPack, and then the process is very simple. I'll explain it with some sample code.

Let's take the Example class in ./src/example.class.ts:

import { SomeThirdParyLibFromNpm } from 'someThirdParyLibFromNpm';
import * as otherNpmLibWithoutTSSupport from 'otherNomLibWithoutTSSupport';
import * as workly from 'workly';

export class Example {
    constructor(
       private myState: AReduxState // example to clarify that we cannot rely on any kind of import, see later
    ) { }

   public someExpensiveTask(): any {
      const whatever = SomeThirdParyLibFromNpm.heavyCpu();
      const whatever += otherNpmLibWithoutTSSupport.otherExpensiveStuff();
      return whatever;
   }
}

workly.expose(Example);

The only thing that we must be aware of, is that this class cannot depend on global/scoped objects of the current project, like any process delegated to a WebWorker. So we cannot import a global const defined/modified at runtime, or rely on libraries like Redux. In such cases I would suggest to pass the values to the constructor like in the above example.

Then we build the UMD with WebPack. Here's my config for webpack.worker.config.js:

const path = require('path');

module.exports = {
  entry: {
    example: './src/example.class.ts'
  },
  resolve: {
    extensions: ['.ts']
  },
  target: 'webworker',
  node: {
    __dirname: true
  },
  output: {
    path: path.join(__dirname, 'src/assets/js'),
    filename: '[name].umd.min.js',
    library: 'Example',
    libraryTarget: 'umd'
  },
  module: {
    rules: [{
      test: /\.ts$/,
      loader: 'ts-loader'
    }]
  },
  mode: "production"
}

Then run in the console:

webpack --config webpack.worker.config.js --progress --colors

Then whenever we want to execute the Example class in a separate thread, thanks to workly we simply need to write in our TS code:

import * as workly from 'workly';

....

const WExample = workly.proxy('/assets/js/example.umd.min.js');
const objWorker = await new WExample(this.myState);
this.whatEver = await objWorker.someExpensiveTask();

In this way we get the double advantage of multi-threading our app, and also some chunks of our code can be downloaded only when needed :-).

PS Thanks for this awesome library!

Feature request: UMD export

Hi! The library is great but in has a disadvantage: it doesn't support Common JS import (i.e. const workly = require('workly');).

I understand that NodeJS doesn't support web workers but CJS is used in other cases, e.g. in Electron (which supports both browser and NodeJS APIs) or bundling JS files using Rollup.

I thing exporting the library in the UMD format is a great solution.

Please mention that this library doesn't support IE11

I've tested the library against Chrome, Edge, Firefox and IE11, unfortunately only IE11 doesn't work properly due to the Proxy issue. Proxy is an ES6 feature and can not be polyfilled.

Is there any solution for this problem?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.