3

As of Chrome 80, you can create module type workers with new Worker("worker.js", { type: "module" }), and then use module features like import in the worker script. However other browsers are still working on support for this, so using it needs feature detection and a fallback.

Is there a convenient and simple way to feature detect module support for workers?

Note on some platforms (e.g. Cordova) creating workers is actually non-trivial and involves a bunch of workarounds, so something that works in pure JS would be ideal.

AshleysBrain
  • 22,335
  • 15
  • 88
  • 124

1 Answers1

12

As every initOptions features, you can use what I call a "dictionary trap".

It is an Object on which you'd set a property getter (the one you want to test), and let this getter switch a boolean when it's been gotten by the constructor you are testing. This works with a lot of such features, and Worker's type is no exception here.

The only things you'll want to be careful with a Worker is to avoid to actually start one (starting even an empty Worker means a new Event loop has to run, a new JS context and these are not small operations) and avoid it to make an useless network request (even a 404 eats resources).

So here is such a tester, using the string "blob://" has a way to avoid both these situations.

function supportsWorkerType() {
  let supports = false;
  const tester = {
    get type() { supports = true; } // it's been called, it's supported
  };
  try {
    // We use "blob://" as url to avoid an useless network request.
    // This will either throw in Chrome
    // either fire an error event in Firefox
    // which is perfect since
    // we don't need the worker to actually start,
    // checking for the type of the script is done before trying to load it.
    const worker = new Worker('blob://', tester);
  } finally {
    return supports;
  }
}

console.log( supportsWorkerType() );
Kaiido
  • 123,334
  • 13
  • 219
  • 285
  • Thanks, that's a nice technique! One question - is this still guaranteed to work even if the content security policy blocks workers being created on `blob:`? I just tried it and it seems to still work in Chrome, but I guess it depends on the browser checking the options before checking the URL. – AshleysBrain Jul 21 '20 at 15:00
  • 2
    @AshleysBrain yes it is guaranteed, they must check this property before trying to fetch the url. – Kaiido Jul 21 '20 at 22:32
  • I like the technique, but I fail to see how it tests "module support for workers"? This only tests that the Worker constructor calls the "type" property getter, not that the Worker implementation supports "import" (vs "importScripts", for example). Are you saying that *only* browsers that support "type: 'module'" will check the type? – user1969177 Jan 07 '21 at 20:48
  • 2
    @user1969177 yes that's the assumption. The [WorkerType enum](https://html.spec.whatwg.org/multipage/workers.html#workertype) can only be one of "classic" or "module". If an implementation checks this param it's because they support module. – Kaiido Jan 07 '21 at 23:44
  • Great answer! I changed 'blob://' to 'data:,' (don't forget the comma) which is basically an empty worker, to avoid security warnings on https pages when running this test, and then added a terminate to stop the empty worker: `new Worker('data:,', tester).terminate();` – Ray Waldin May 03 '21 at 18:54
  • @RayWaldin but we want this to throw. Failing to throw before the Worker is initialized means that all its global scope and event loop and js instance etc. have to be initialized. That's a **big** amount of work for the computer. Thanks to the fake `blob://` URL, none of it is done, the process ends at parsing the URL. – Kaiido May 03 '21 at 23:23