简体   繁体   中英

Load web workers dynamically. Errors resolving relative imports

Context:

I am loading several web workers in a dynamic way by using blob-based URIs. In my problem, this strategy is required because workers' content is partially generated using a template-based solution. See (1) in code

Problem:

Web workers include import sentences with relative paths. As they are loaded using the Worker constructor and it does not allow to specify a base path, relative imports cannot be properly resolved as absolute references. As a result, the worker loading process fails with no console message (I guess a 404 error is under the hood).

Example:

In this example we have created the following set of resources:

index.html
/scripts
  /helpers
     helper.x.js
     helper.y.js
  /loader
     loader.js
  /workers
     worker.x.js

We have created a dynamic loader in loader.js:

function Loader () {

  function asCode (text) { ... }

  async function load (path) {
    let response = await fetch (path)
    let text     = await response.text ()
    let code     = asCode (text) // (1)
    let blob     = new Blob ([code], { type: 'text/javascript' })
    let url      = URL.createObjectURL(blob)
    let worker   = new Worker (url, { type: 'module' })
    return worker
  }

  return { load }
}

export default Loader ()

The worker in worker.x.js imports two naive helpers X & Y by using relative paths. Please, notice that if the path in import sentences where absolute (http://...) the problem described here would not appear:

  import X from '../helpers/helper.x.js'
  import Y from '../helpers/helper.y.js'

  X.fx ()
  Y.fy ()

Finally, in index.html we load the worker:

<body>
   <script type="module">
     import Loader from './scripts/loader/loader.js'
     let WX = Loader.load ('./scripts/workers/worker.x.js')
   </script>
</body>

Please, visit this replit to explore the project.

Question:

Is there any way to load web worker scripts in a dynamic way specifying the base path that must be used to resolve relative paths in inner import sentences? For me, an extension of the Worker constructor with a new base parameter like this would be perfect:)

let WX = new Worker (path, { type: 'module', base: 'the-base-path' })

You could use your asCode method to also inject the base URI.

In your template, insert a marker in front of your relative URLs that you'll replace at the time of building your blob.
To get the "fake" base URL as an absolute one, you can check the Response object you get from fetch() and extract the directory path from its .url property.

Now, your "dynamic" module imports absolute paths and everyone is happy: loader.js

function Loader () {

  function asCode (text, baseURI) {
    return baseURI
      ? text.replace(/\$base_url\$/g, baseURI)
      : text;
  }
  
  async function load (path) {
    let response = await fetch (path)
    let text     = await response.text ()
    let absolute = response.url;
    // remove the worker script's file name to get its base URI
    let href     = absolute.substring(0, absolute.lastIndexOf('/'));
    let code     = asCode (text, href)
    let blob     = new Blob ([code], {type: 'text/javascript' })
    let url      = URL.createObjectURL(blob)
    let worker   = new Worker (url, { type: 'module' })
    return worker
  }

  return { load }
}

export default Loader ()

And worker.x.js now looks like

import X from '$base_url$/../helpers/helper.x.js'
import Y from '$base_url$/../helpers/helper.y.js'

X.fx ()
Y.fy ()

Updated repl.it.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM