uwork

1.1.0 • Public • Published

uwork

Web workers made easy. Perfect for math intensive tasks as it uses the CPU in parallel:

CPU in async vs in parallel

Web workers are awesome and this project is taking a subset of that and making it trivial to implement. This is for functions that take N arguments, process them and then return a value once.

Getting started

Install with npm:

npm install uwork

Or include the script in your website:

<script src="https://cdn.jsdelivr.net/npm/uwork@1/uwork.js"></script>

Then create the work that you want performed and wrap it with a function called uwork. It can be either a sync or async function. Let's calculate Pi with the Monte Carlo method as an example:

// Create the piece of work to be performed
// It can be sync or async
const findPi = uwork((iterations = 10000) => {
  let inside = 0;
  for (var i = 0; i < iterations; i++) {
    let x = Math.random(), y = Math.random();
    if (* x + y * y <= 1) inside++;
  }
  return 4 * inside / iterations;
});

Finally do the actual work and handle the returned promise:

// Run this inside an async context:
const pi = await findPi(200000000);

Regardless of whether the function you create is sync or async, the returned function of uwork() will always be async. This is the return order:

uwork(fn) => function(args) => Promise(value)

See demo in JSFiddle.

Options

There's only a single option so far: timeout (ms). Establish a maximum time for the web worker to perform some job, then the promise is rejected. If set to 0, false or a falsy value then there is no timeout. Defaults to undefined.

// Limit it to 10s
uwork.timeout = 10000;
 
// ...

Communication

To call your parallel function it's always the same way; pass the arguments to the returned function from uwork() and await for the return value:

const work = uwork(fn);
const result = await work(args);

Now, to send that result from within the fn(), you can return a value either from a sync or an async function. Since many math intensive processes are synchronous, this is a great way to unblock the main thread while doing heavy work in a secondary thread:

// Simple return for sync operations. This will be made parallel and thus async
var work = uwork(function intensive(args) {
 
  // heavy work here
 
  return 42;
});

Using Async/Await it also has a very clean syntax:

// Using promises
var work = uwork(async (number = 10000) => {
 
  // heavy work here
 
  return 42;
});

Note that the code above is equivalent to the following:

// Using promises
var findPi = uwork((number = 10000) => {
  return new Promise((resolve, reject) => {
 
    // heavy work here
 
    resolve(42);
  });
});

The coolest thing is that the function interface is compatible with normal Javascript code, you just wrap it around:

// Perform the work in a single thread but async
const work = async () => {
 
  // heavy work here
 
  return 42;
};
 
const res = await Promise.all([work(), work(), work(), work()]);
// Just wrap uwork() around it to make it parallel
const work = uwork(async () => {
 
  // heavy work here
 
  return 42;
});
 
const res = await Promise.all([work(), work(), work(), work()]);

Error handling

Follows standard promise error handling:

try {
  const pi = await findPi(20000);
} catch (error) {
  console.error(error);
}

To reject the operation in both ways you can either return an Error (which will be stringified, read extra section) or throw it:

const findPi = uwork((number) => {
  if (number === 0) {
    // return new Error('Cannot iterate 0 times');
    throw new Error('Cannot iterate 0 times');
  }
 
  // heavy work here
});

Extra

There are some things that you should know. While Web Workers are great, they also have limitations. The main ones [for practical purposes] are:

Functions should be self-contained

This won't work for example:

var external = whatever => whatever;
 
var worker = uwork(function(){
  return external('Peter');
});

This is a limitation of the way we simplify web workers. We are basically getting the function, converting it into a string, creating a virtual script and executing it there (not too different from eval).

Values should be able toString()

Both the arguments you are passing and the result of the operation.

Security

Treat this as if it was using eval() internally or dig into the code and edit this document if you know more than me.

Don't build anything dynamically (from the previous limitations is quite difficult anyway), just rely on this for math or CPU-intensive processes. Clean and validate your arguments.

Native workers

Native workers can communicate over time several messages, so this library is somewhat limiting that in exchange for a much simpler API. If you like this I recommend you dig into them.

Credit

Learned from (Blob() & URL.createObjectURL

Dependents (0)

Package Sidebar

Install

npm i uwork

Weekly Downloads

3

Version

1.1.0

License

MIT

Unpacked Size

14.2 kB

Total Files

5

Last publish

Collaborators

  • franciscop