mrkeyoor.com_
Wed 23 Sept 00:37 UTC
npmWeb Frontendupdated 22 Sept 2026

promise-worker-transferable review

promise-worker-transferable 1.0.4 wraps Web Worker and Service Worker messages in promises and adds transfer lists to both directions. Our browser bundle measured 5.1 KB minified and 2 KB gzipped. The page side calls `postMessage()` and receives a correlated promise; the worker registers one handler and may return a value, promise, or `withTransferList()` result. This 2016 fork exists because upstream promise-worker declined transferable objects. It is a message bridge, not a worker pool, RPC schema, timeout system, or Node worker_threads library.

Verdict

promise-worker-transferable 1.0.4 installed in 0.6 seconds and bundled to 5.1 KB minified in our sandbox, but it has no bundled types, timeout, or release after 2016. Keep it in a working legacy transfer path; choose Comlink or a worker-pool library for new code.

We installed it

Lab card: what happened when we installed promise-worker-transferableScreenshot of promise-worker-transferable documentation
Install✓ · 0.6s4 packages on disk · 1 MB
ImportESM import works · require() works · CommonJS package
Browser2 KBgzipped (5.1 KB minified), bundled with esbuild
Typesno TypeScript types found
Known vulns00 critical · 0 high · 0 moderate · 0 low (npm audit)

Answers from our run

Does promise-worker-transferable install cleanly?

Yes. In a fresh container with an empty cache, npm install promise-worker-transferable finished in 0.6s, leaving 4 packages and 1 MB on disk. npm audit reported no known vulnerabilities.

How much does promise-worker-transferable add to a browser bundle?

2 KB gzipped (5.1 KB minified) when the whole package is bundled for the browser with esbuild. Importing only part of it is usually smaller.

Does promise-worker-transferable work with both ESM and CommonJS?

Yes. Both import 'promise-worker-transferable' and require('promise-worker-transferable') worked in Node 22 in our run. The package is published as CommonJS.

Does promise-worker-transferable include TypeScript types?

No type declarations were found in our install, so TypeScript users need their own declarations.

promise-worker-transferable or comlink: which should you use?

comlink: Use it for maintained worker RPC with method proxies, transfers, ESM, and TypeScript declarations. promise-worker-transferable 1.0.4 installed in 0.6 seconds and bundled to 5.1 KB minified in our sandbox, but it has no bundled types, timeout, or release after 2016.

When should you not use promise-worker-transferable?

New worker code needs current maintenance: all three releases are from September 2016 and the last repository push was in July 2020

API stability4/5The public surface has a constructor, `postMessage(message, transferList)`, one worker registration function, and `withTransferList(value, transferList)`. Three releases landed within two days in September 2016 and nothing has changed since, so existing behavior is fixed. The source also exposes `terminate()` without documenting its pending-promise behavior, which makes the practical lifecycle API wider and less dependable than the README suggests.
Docs3/5The README shows both bundle entry points, plain and promise results, transfer lists in each direction, structured messages, error propagation, type-based routing, and the wait for Service Worker control. It admits stack traces do not cross and warns ordinary messages may be slower than upstream. It omits buffer detachment, timeouts, callback retention, terminate behavior, the Service Worker empty-list edge, TypeScript status, and modern module guidance.
Maintenance1/5Versions 1.0.2 through 1.0.4 were all published on 2016-09-22 or 2016-09-23. GitHub shows the last push on 2020-07-01, only 11 stars, and no open issues or pull requests. GitHub does not mark the repository archived. Even so, no release, browser-matrix update, module modernization, or lifecycle fix has appeared for years. High transitive download traffic does not change that maintenance record.
Ecosystem2/5The npm endpoint counted 3,712,343 weekly downloads, yet the repository has 11 stars and the package provides no declarations, ESM build, plugins, framework adapters, or worker-pool integration. Its two dependencies supply promise detection and a polyfill. Modern worker projects more often use Comlink, typed worker wrappers, or bundler-native module workers, none of which shares this single-handler API. The volume appears driven by dependency trees rather than a broad direct-user community.

Use it if

  • A legacy browser worker already uses promise-worker and large ArrayBuffers must transfer instead of copy
  • Request and response IDs should be hidden behind one promise-returning call
  • One worker handler is enough and message routing by a `type` property is acceptable
  • The same small bridge must cover Web Workers and a controlling Service Worker
Skip it if

Setup reality

Our fresh install of promise-worker-transferable 1.0.4 completed in 0.6 seconds and left 4 packages using 1 MB on disk. It has 2 direct dependencies, no peers, no TypeScript declarations, and 0 known audit vulnerabilities. The Apache-2.0 package is 68 KB unpacked.

Build two entry points. The page bundle imports promise-worker-transferable; the separate worker file imports promise-worker-transferable/register. CommonJS has no exports map, though both require() and ESM import worked through Node 22 interop in our check. esbuild produced 5.1 KB minified and 2 KB gzipped for the page import. No credentials, config file, native compiler, or service is involved.

A transferred ArrayBuffer is detached from its sender, leaving byteLength at 0. Transfer only data that side has finished using. The worker handler may return a plain value or promise. Wrap a response with withTransferList(value, list) to transfer its buffer back. Rejections preserve only the message string, and the worker logs failures with console.error; return an explicit result object when callers need codes or structured details.

There is no built-in deadline. If a worker dies or never replies, its promise and callback entry remain. terminate() forwards to the worker but does not reject in-flight calls, so application code must settle or replace its own pending operations. On the Service Worker controller path, pass [] when there is no payload transfer list; the implementation concatenates that value with its MessagePort, and an omitted value can produce an invalid list entry.

Patterns

Await one worker response send-worker-request

const PromiseWorker = require('promise-worker-transferable')
const worker = new Worker('worker.js')
const bridge = new PromiseWorker(worker)

const reply = await bridge.postMessage('ping')

The page and worker are separate bundles. This entry belongs only in the page-side bundle.

Reply from the worker entry register-worker-handler

const register = require('promise-worker-transferable/register')

register((message) => {
  if (message === 'ping') return 'pong'
  return Promise.resolve(compute(message))
})

One handler receives every request. Route multiple operations inside it with your own message field.

Move pixel bytes without copying transfer-buffer-to-worker

const imageData = ctx.getImageData(0, 0, width, height)
const result = await bridge.postMessage(
  imageData, [imageData.data.buffer]
)
console.log(imageData.data.buffer.byteLength) // 0

Transfer detaches the sender's buffer. Any page code that still needs those bytes must copy them first.

Return a worker-owned buffer transfer-buffer-from-worker

register((message, withTransferList) => {
  const output = processPixels(message)
  return withTransferList(output, [output.data.buffer])
})

`withTransferList` is the second handler argument and can also be returned after asynchronous work.

Dispatch messages by type route-worker-operations

register((message, withTransferList) => {
  switch (message.type) {
    case 'resize': {
      const out = resize(message)
      return withTransferList(out, [out.buffer])
    }
    case 'stats': return computeStats(message)
    default: throw new Error(`unknown type: ${message.type}`)
  }
})

A thrown error comes back as a rejected promise containing a fresh Error with only the original message.

Return structured failures yourself preserve-error-fields

register(async (message) => {
  try {
    return { ok: true, value: await work(message) }
  } catch (error) {
    return { ok: false, code: error.code, message: error.message }
  }
})

const result = await bridge.postMessage(job)
if (!result.ok) handle(result.code)

Normal rejection loses error names, codes, stacks, and custom properties across this bridge.

Stop awaiting a silent worker add-worker-timeout

function withTimeout(promise, ms) {
  let timer
  const timeout = new Promise((_, reject) => {
    timer = setTimeout(() => reject(new Error('worker timeout')), ms)
  })
  return Promise.race([promise, timeout]).finally(() => clearTimeout(timer))
}

await withTimeout(bridge.postMessage(job), 10000)

Racing stops the caller's wait but does not cancel worker work or remove the package's callback entry.

Track calls before termination terminate-worker

const pending = new Set()
function send(message, transfers) {
  const task = bridge.postMessage(message, transfers)
  pending.add(task)
  return task.finally(() => pending.delete(task))
}
function shutdown() {
  bridge.terminate()
  pending.clear()
}

`terminate()` is undocumented and leaves in-flight package promises unsettled. Your wrapper needs its own rejection policy.

Pass an explicit empty transfer list message-service-worker

const bridge = new PromiseWorker(navigator.serviceWorker)
const reply = await bridge.postMessage({ type: 'status' }, [])

On the Service Worker path, `[]` avoids concatenating an omitted value beside the required MessagePort.

Wait until the page has a controller wait-for-service-worker

await navigator.serviceWorker.register('sw.js', { scope: './' })
if (!navigator.serviceWorker.controller) {
  await new Promise((resolve) =>
    navigator.serviceWorker.addEventListener('controllerchange', resolve, { once: true })
  )
}
const bridge = new PromiseWorker(navigator.serviceWorker)

Registration alone does not guarantee control. The Service Worker can call `clients.claim()` during activation to take over sooner.

Alternatives

PackageRegistryPick it when
comlinknpmUse it for maintained worker RPC with method proxies, transfers, ESM, and TypeScript declarations
threadsnpmUse it for typed worker APIs, observables, and pools in browser or Node projects
piscinanpmUse it for a managed Node worker_threads pool with queuing and operational controls

More web frontend guides

postcss · react · react-dom · tailwindcss · htmlparser2 · tailwind-merge · the whole shelf →

How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.