73

I am using ES2015 Import / Export modules.

In my worker file, when I try to import functions like I normally do:

worker.js

import { a, b, c } from "./abc.js";

I get the error: SyntaxError: import declarations may only appear at top level of a module

As I am exporting functions in my module 'abc.js', I am not sure how to use them using the old (& apparently on its way out) syntax:

self.importScripts( "/app/abc.js" );

So, my question is, how do we use the new import module syntax with workers?

Second question is, what does importScripts import into when it imports from a module in where is there is no global object parent being exported?

Kayote
  • 14,579
  • 25
  • 85
  • 144

5 Answers5

94

ES2015 modules in Workers are available in Safari and in Chromium browsers.

If other browsers / versions are your target, you still need to use importScripts().

When available, you create a module-worker like this:

new Worker("worker.js", { type: "module" });

See: https://html.spec.whatwg.org/multipage/workers.html#module-worker-example

These are the bug-reports for each browser:

  • Firefox:
    Workers: Available since version 114 ✔️
    Service Workers: in development
  • Chromium Browsers:
    Dedicated Workers: Available since version 80 ✔️
    Shared Workers: Available since version 83 ✔️
    Service Workers: Available since version 91 ✔️
  • Webkit:
    Safari Desktop: Available since Safari 14.1 ✔️
    Safari Mobile (iOS): Available since Safari 15 ✔️

The relevant Can I Use live compatibility data: https://caniuse.com/?search=module%20worker

Tobias Buschor
  • 3,075
  • 1
  • 22
  • 22
  • 3
    How to know when it will be supported? – Yairopro Dec 26 '17 at 16:20
  • 1
    See my updated Answer. Chromium is actively developing it. – Tobias Buschor Dec 27 '17 at 22:28
  • Ah, so only the new Worker(..) needs to specify "module". The actual worker code can have import/export as normal. Is this right? Sounds like node's .mjs :) – backspaces Jul 24 '18 at 16:35
  • It’ll be shipped with Chromium 80 and Chrome 80 is planned to be stable on February 4, 2020. Alright……. – Константин Ван Dec 19 '19 at 09:13
  • Damn, [82 for `SharedWorker`s](https://www.chromestatus.com/feature/5169440012369920)? Okay, [they decided to skip the 82](https://www.techradar.com/news/chrome-82-skipped-as-google-reforms-release-cycle), and [the 83’s planned to be stable on May 19, 2020](https://www.chromestatus.com/features/schedule). – Константин Ван Apr 27 '20 at 04:04
  • What about breaking a workers code into many modules while it stays as a single module? Is that possible? I think the above is already it. Once you define a module Web Worker and you can `import` `export` other modules in it, otherwise can do same with `importScripts`. Hence a single Web Worker can be a multi module module, right? – sçuçu Oct 27 '21 at 21:34
  • What is a 'module-worker' as mentioned in this answer? – mikemaccana Dec 05 '22 at 12:39
11

2020:

Chrome 80 has shipped module workers in February 2020 (and Chrome 82 will ship modules for shared workers). Firefox/Safari do not support those features for now: tests

You may want to use import-from-worker lib to do the heavy lifting for you (for now, you need to check the support for modules in workers and do the fallback by yourself).

saravana priyan
  • 463
  • 6
  • 19
jakub.g
  • 38,512
  • 12
  • 92
  • 130
3

ES Modules in workers are already available in Chrome, enabling Experimental Web Platform Features, using the proper flag when launching chrome:

chrome.exe --enable-experimental-web-platform-features

This is the syntax you need to use to load the worker script as a module :

new Worker( 'my-worker.js', { type : 'module' } );

This feature has been in development for almost ayear, and should be available soon, without the need of special flags, however, there is no official release date yet.

colxi
  • 7,640
  • 2
  • 45
  • 43
  • Also instead starting with a flag there option for that in `chrome://flags/` then search for `Experimental Web Platform features` or something like that – Blanket Fox Apr 12 '21 at 02:04
3

As of Nov 21 importing modules in workers still seems to be flaky. One solution is to use rollup to generate an IIFE from your worker as follows:

//worker.js
import { MyModule } from 'my-module.js'
onconnect = async (e) => {
    var port = e.ports[0];
    MyModule.func()

    port.onmessage = (e) => {
        port.postMessage("Hi App");
    }
}

//rollup config
export default [
{
        'input': 'worker.js',
        'output': {
            'file': 'dist/worker.js',
            'format': 'iife'
        },
 },
]


//dist/worker.js (rollup output)

(function () {
    'use strict';
    //MyModule code here, generated by rollup
    
    MyModule.func()
    onconnect = async (e) => {
        var port = e.ports[0];
        port.onmessage = (e) => {
            port.postMessage("Hi App");
        };
    };

}());

//main app

const worker = new SharedWorker("/dist/worker.js");
worker.port.onmessage = (e) => {
    console.log('Message received from worker: ' + e.data);
}
worker.port.postMessage("Hi worker");

Essentially rolllup is doing the work that browsers should be doing. This is working well for me. Of course the code size is increased because the module code is getting copied into worker as well. But it is still DRY as the code is being generated by rollup.

kargirwar
  • 536
  • 2
  • 7
  • 19
-4

for me assigning to self. worked well. I've put import to another js file: abcImported.js

import { a, b, c } from "./abc.js";

export {  a, b, c };

and in the service worker:

self.a = require('abcImported.js').a;
self.b = require('abcImported.js').b;

in this way, it is accessible inside the worker. (tested in chrome)

Kate Kasinskaya
  • 823
  • 10
  • 10