npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

inline-webworker-functional

v2.0.0

Published

execute your time consuming function asynchronously in the background

Downloads

11

Readme

inline-webworker-functional

Execute your time consuming JS functions asynchronously in the background, works with both NodeJS and browser environment. When in browser, it creates inline web worker under the hood; When in NodeJS, it creates child process under the hood.

Motivation

Call your functions asynchronously, without worrying anything about the underlying web worker API or child process API at all, like creating separate JS file, message passing etc.

Installation

NodeJS Environment

install from NPM

npm install inline-webworker-functional

import to your JS file

const {makeSimpleWorker, makeStatefulWorker, terminateStatefulWorker} = require("inline-webworker-functional/node")

Webpack Environment

install from NPM

npm install inline-webworker-functional

import to your JS file

const {makeSimpleWorker, makeStatefulWorker, terminateStatefulWorker} = require("inline-webworker-functional/browser")

Use Directly in Browser

<script src="https://unpkg.com/inline-webworker-functional/browserlib.js"></script>

Usage

Basic usage

Make a function asynchronous:

function lotsOfWork(x, y) {
    let s = 0;
    for (let i = 0; i < x; ++i) {
        for (let j = 1; j < y; ++j) {
            s += i / j;
        }
    }
    return s;
}

(async()=>{
    // create a asynchronous version of your function
    const lotsOfWorkAsync = makeSimpleWorker(lotsOfWork);
    
    // execute your function in the background
    const result = await lotsOfWorkAsync(1e4, 1e4);
    console.log(result); 
})();

Due the to the limitation of web worker, the job function (the function to convert) must be completely self-contained, without any reference to external variables/functions/classes. If your job function requires other helper functions/classes as subroutines, please inline those dependencies into your function, like this:

function complexWork(x) {
    class Circle {
        constructor(r) {
            this.r = r;
        }
        getArea() {
            return Math.PI * this.r * this.r;
        }
    }
    return new Circle(x).getArea();
}
(async()=>{
    const complexWorkAsync = makeSimpleWorker(complexWork);
    const result = await complexWorkAsync(2);
	console.log(result); // prints 12.566370614359172
})();

And also, the worker task function cannot take parameters of arbitrary type. The parameters must be compatible with the structured clone algorithm.

Advanced usage: keeping internal state

the async function returned by makeSimpleWorker does the following things: When invoked, this async function creates a worker, executes it, and then destroys it. It works well in most cases. But in some scenarios, you may want more fine controls, like:

  • you want to keep some internal states in your worker
  • you simply don't want your worker to be automatically destroyed

In these cases, you will need to call makeStatefulWorker instead.

First, you need to have a worker task factory function that returns a dictionary of worker tasks.

function createStatefulWorkerTasks() {

    let balance = 0;

    function save(x) {
        balance += x;
        return balance;
    }

    function withdraw(x) {
        if(balance < x) throw new Error("insufficient balance");
        balance -= x;
        return balance;
    }

    return {save, withdraw};
}

Then, call makeStatefulWorker on your factory function. It asynchronously returns a worker object.

(async()=>{
    const worker = await makeStatefulWorker(createStatefulWorkerTasks);
})();

Now the worker object contains save and withdraw attribute, those are asynchronous versions of the corresponding worker tasks.

(async()=>{
    const worker = await makeStatefulWorker(createStatefulWorkerTasks);
    console.log(await worker.save(200)); // prints 200
    console.log(await worker.withdraw(300)); // throws an error with message "insufficient balance"
})();

A stateful worker doesn't automatically destroy itself. You need to manually destroy it by calling terminateStatefulWorker

terminateStatefulWorker(worker) // where "worker" is the object return by "makeStatefulWorker"

Advanced usage: invoking host functions from worker

So far, the worker is passively communicating with the host. In other words, the host need to first request something to trigger the workers response. But in some scenarios, the worker need to actively communicate with the host, even though the host is not invoking any worker task at all. For example, this is what you want:

function foo(msg) {
    console.log('worker says:', msg);
}

function workerTask() {
    setInterval(()=>{
        foo("hello");
    }, 500);
    return {};
}

(async ()=>{
    const worker = await makeStatefulWorker(workerTask);
})();

However, when you execute this, you will get an error because foo is a function defined only in the host but not in the worker. You need to explicit specify that you want the worker to be able to trigger foo function on the host, by passing foo to makeStatefulWorker in the second parameter. Here is how you can do it:

function foo(msg) {
    console.log('worker says:', msg);
}

function workerTask() {
    setInterval(()=>{
        foo("hello");
    }, 500);
    return {};
}

(async ()=>{
    const worker = await makeStatefulWorker(workerTask, {foo});
})();

In this way the worker can trigger the foo function on the host. However there are certain limitations on the functions to trigger:

  • it's parameters must be compatible with structured clone algorithm
  • it must not have any return value

Advanced usage: importing libraries in worker

Browser and Webpack environment

In browser or Webpack environment, there is currently not supported. The worker must work entirely on its own, without importing any libraries.

NodeJS environment

In NodeJS environment, the worker can import other libraries. But you have to put the import statement within the worker task function. However, the import keyword refuses to appear within function body, it must sits at the top of the JS/TS file. But that's fixable, you can use the old require semantics instead, like this:

function workerTask() {
    const fs = require("fs");
    
    // fs library can be used in worker now
}

makeSimpleWorker(workerTask);