Web Workers


Uncategorized

Updated Apr 26th, 2023

MDN docs here

Think of the worker as another thread in which you can offload computationally-intensive calculations. Fairly easy to implement.

In a worker you don’t have access to the DOM or window or global variables. You can log to the console from the worker but certain methods like “.toLocaleString()” do not work.

Can the same worker file be used for multiple slates or does each slate need its own? I used the same worker for multiple slates.

Can leverage addEventListeners like message and error onto the worker

Will often use the keyword “self” inside of the web worker and the methods on the self object.

self.AddEventListener
self.postMessage('Closing the web worker')
self.close()


Your worker file can only communicate back with the main JavaScript program using messaging

onmessage() // receive data from main thread/program
postmessage() // send data bac to the main thread/program

The worker file needs to be loaded from the same origin (domain, port and protocol).

They worker file will not work if you serve the page using the file protocol (file://), you need to serve them // through HTTP(S) using a web server. You cannot just run on your file system you need to run on a development server (for example, live server) or deployed server.

“Uncaught DOMException: Failed to construct ‘Worker’: Script at ‘file:///C://whatever/whatever/worker.js’ cannot be accessed from origin ‘null’.”

Good browser support although not 100% so you can use a conditional to make sure it is available.

if (typeof Worker !== 'undefined') {//..workers are available}

There is an onerror we can access with event.message

Web Workers are launched and if they do not stay in listening mode for messages through worker onmessage or by adding an event listener, they will be shut down as soon as their code is run through completion.

A Web Worker can be stopped using its terminate() method from the main thread, and inside the worker itself using the global method close()

Can load libraries using the importScripts() global function defined in their global scope

APIs are available in Web Workers, although no DOM so no window and document objects.

With React and TypeScript

Implementations will leverage useEffect hook.

Example with Vanilla JS

// inside a handleClick function
  // define object to send to worker
  let workerData = {
    salaryMax,
    maxLineupsToCheck,
    minimumPts,
    sitStarts,
    trimmedAndBenchedPlayerData
  }
  // instantiate worker to run computationally expensive loop
  // sending it the necessary data
  const myWorker = new Worker("worker.js")
  myWorker.postMessage(workerData)
  // Add Listener for cancel button
  cancelBtn.addEventListener("click", () => {
    generateBtn.disabled = false
    loadingContainer.classList.remove("active")
    dotsLoading.classList.remove("active")
    cancelBtn.classList.remove("active")
    myWorker.terminate()
    generateBtn.focus()
  })
  // receive the data back from the worker
  myWorker.onmessage = function (e) {
    //console.log(e.data)
    runTime = e.data.runTime
    foundLineup = e.data.testLineup
    i = e.data.i
    conditionsMet = e.data.conditionsMet
    testLineupProjectedPts = e.data.testLineupProjectedPts
    testLineupSalary = e.data.testLineupSalary
  }

In the worker itself

onmessage = function (e) {
  //define necesssary variables in worker scope
  let conditionsMet = false
  let i = 1
  let maxLineupsToCheck = e.data.maxLineupsToCheck
  let result
 
  do {
    // create Random lineup, return it to the main thread of meets conditions
    result = whatEvFunction(maxLineupsToCheck)
    // increment i to check again
    i++
  } while (i <= maxLineupsToCheck && conditionsMet == false)
  
  function whateEvFunction(maxLineupsToCheck) {
   // code here
  }
  // define object to send back
  let outputObject = {
    conditionsMet: result.conditionsMet,
    testLineup: result.testLineup,
    i: i - 1,
  }
  // send data back from worker
  postMessage(outputObject)
}