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

worker-proxy

v2.1.4

Published

thread-local proxy for a given service spawned in dedicated Worker thread

Downloads

29

Readme

worker-proxy Join the chat at https://gitter.im/ZenyWay/worker-proxy

NPM build status coverage status Dependency Status

spawn a service in a Web Worker and proxy it from the current thread

the proxy's service property resolves to a service object with the same interface as that of the original service running in the Worker thread. only methods of the service object and its prototype chain are proxied (not all properties).

the proxy exposes a terminate method that allows to properly shut down the service through a onterminate handler in the Worker thread before definitively terminating the Worker.

API v2.1 stable

ES5 and Typescript compatible. Coded in Typescript 2.

specs

run the unit tests in your browser.

example

a live version of this example can be viewed here in the browser console, or by cloning this repository and running the following commands from a terminal:

npm install
npm run example

the files of this example are available here.

module: my-service

sample third-party service module that we want to spawn in a dedicated Worker and proxy in the main thread. the service module is not part of the worker-proxy module.

/**
 * this is the service that will be spawned in a Worker thread
 * and that will be proxied in the main thread.
 * in this example, the service is created by a factory,
 * and it exposes both sync and async (Promise-returning) methods
 * for illustration purposes.
 * it expects to be shut down by calling its async `stop` method.
 */
export interface Service {
  toUpperCase (text: string): string // say this converts text to upper case
  anotherMethod (arg: any): any // any other service method
  stop (): Promise<void> // say this must be called to shut down the service
}

export interface ServiceFactory {
  (spec?: Object): Promise<Service>
}

const newService: ServiceFactory // = ... implementation left out
export default newService

file: worker.ts (WorkerGlobalScope)

this script will be spawned from the main thread in a dedicated Worker thread. it creates and initializes the service from the my-service module, and then hooks it up so that it can be proxied from the main thread. In doing so, it defines an onterminate handler that will be called when the proxy is terminated in the main thread and that allows to properly shut down the service before the Worker is terminated.

The onterminate handler may return a Promise that will be resolved or rejected in the main thread, allowing the latter to handle failure of service shut-down before eventually forcing the Worker to terminate.

import hookService from 'worker-proxy/dist/worker'
import newService from 'my-service'

// create and initialize the service
const spec = { /* service configuration options */ }
const service = newService(spec) // Promise<Service>

// define an `onterminate` handler to properly shut down the service
// before this Worker is terminated.
// the return value will be sent back to the main thread.
function onterminate () {
  return service
  .then(service => service.stop()) // resolve or reject back to main thread
}

// hook up the service so it can be proxied from the main thread
// and wait until the proxy's terminate method is called in the main thread
service
.then(service => hookService({
  worker: self,
  service: service,
  onterminate: onterminate
}))

NOTE: the above approach requires prior bundling of dependencies into the worker code, e.g. with browserify. see below for a webworkify-like example, allowing the worker-script to require its dependencies 'live'.

since npm version 2.1.0 (API 1.1) it is additionally possible to restrict the proxied service methods to a subset of the original service methods:

service
.then(service => hookService({
  worker: self,
  service: service,
  methods: [ 'toUpperCase', 'stop' ], // only expose service#toUpperCase and service#stop
  onterminate: onterminate
}))

file: index.ts

this script runs in the main thread. it spawns the worker.ts script in a dedicated Worker thread and proxies it in the main thread.

the resulting proxy resolves to a service object with the same interface as that of the original service running in the Worker thread. note that in its current implementation, only enumerable methods of the service are proxied.

to shut down the service and terminate the Worker, the proxy's terminate method can be called as in this example.

import newServiceProxy from 'worker-proxy/dist/proxy'
import { Service } from 'my-service' // only import the interface for casting
const log = console.log.bind(console)

// proxy and spawn the Worker
const proxy = newServiceProxy<Service>('worker.ts')
const terminate = proxy.terminate.bind(proxy)

// unwrap the Promise to access the proxied service
proxy.service
.call('toUpperCase', 'Rob says wow!') // or .then(service => service.toUpperCase('Rob says wow!'))
.tap(log) // "ROB SAYS WOW!"
.then(terminate) // shut down service and terminate Worker
.catch(err => log(err) || proxy.kill()) // log shutdown error and force Worker termination

note that using the Service interface as type for the proxied service object is not strictly correct, since all methods of the latter are asynchronous, i.e. return a Promise, while some methods of the Service instance running in the worker are synchronous. However, in the context of the above example, the approximation is not relevant.

require in worker script

the above approach requires prior bundling of dependencies into the worker code, e.g. with browserify. this typically results in unnecessarily bloating the code because bundles are generated both for the main script and the worker script, and common dependencies are bundled in both.

worker-proxy builds on webworkify to optionally spawn a Worker that can require its dependencies 'live': both the main and worker scripts can be bundled together with browserify, together with all required dependencies.

to enable this, the main script should require the worker script, which should export its code as a function taking a single argument, self, without any return value.

the main and worker scripts from the above example are modified accordingly for illustration:

file: worker.ts (WorkerGlobalScope)

to enable 'live' require calls from the worker thread, simply wrap its code into a function that takes a single argument (self), and export that function:

import hookService from 'worker-proxy/dist/worker'
import newService from 'my-service'

export = function (self) {
  // create and initialize the service
  const spec = { /* service configuration options */ }
  const service = newService(spec) // Promise<Service>

  // define an `onterminate` handler to properly shut down the service
  // before this Worker is terminated.
  // the return value will be sent back to the main thread.
  function onterminate () {
    return service
    .then(service => service.stop()) // resolve or reject back to main thread
  }

  // hook up the service so it can be proxied from the main thread
  // and wait until the proxy's terminate method is called in the main thread
  service
  .then(service => hookService({
    worker: self,
    service: service,
    onterminate: onterminate
  }))
}

file: index.ts

to enable 'live' require calls from the worker thread, simply require the worker script in the main script:

import worker = require('./worker')
import newServiceProxy from 'worker-proxy/dist/proxy'
import { Service } from 'my-service' // only import the interface for casting
const log = console.log.bind(console)

// proxy and spawn the Worker
const proxy = newServiceProxy<Service>(worker)
const terminate = proxy.terminate.bind(proxy)

// unwrap the Promise to access the proxied service
proxy.service
.call('toUpperCase', 'Rob says wow!') // or .then(service => service.toUpperCase('Rob says wow!'))
.tap(log) // "ROB SAYS WOW!"
.then(terminate) // shut down service and terminate Worker
.catch(err => log(err) || proxy.kill()) // log shutdown error and force Worker termination

other example

another webworkify-based example can be found in this gist, and demonstrates how to spawn opgp-service in a worker, with the main-thread proxy, the spawned service and all their dependencies all bundled in a single file.

CONTRIBUTING

see the contribution guidelines

LICENSE

Copyright 2016 Stéphane M. Catala

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and Limitations under the License.