Search code examples
javascriptmultithreadingweb-workerchild-process

JavaScript multiple child-processes in one function


i'm curious if it's possible to run multiple web workers / child proceeses in one function.

So i have a function which calls multiple (main-thread blocking, heavy-loading) algorithms. It takes quite long, so I want them to run parallel / multiple theads in order to save time.

  function calculateThings(division, colorVal) {
    const resultArray = imageToArray(img, division);

    /* Worker 1 / Child-Process 1 */
    const resultDefaultAlgorithm = algorithmDefault(resultArray, colorVal);
    const resultDefaultImage = arrayToImage(resultDefaultAlgorithm, colorVal);
    /* Worker 2 / Child-Process 2 */
    const resultFAlgorithm = algorithmF(resultArray, colorVal);
    const resultFImage = arrayToImage(resultFAlgorithm, colorVal);
    /* Worker 3 / Child-Process 3 */
    const resultFSAlgorithm = algorithmFS(resultArray, colorVal);
    const resultFSImage = arrayToImage(resultFSAlgorithm, colorVal);

    return [resultDefaultImage, resultFImage, resultFSImage];
  }

As you see, i want two function calls for each web worker / child process. Is this possible?

Looking at Web Workers, this only seems possible when each worker has it's own file, which i don't want / don't really know how to do.


Solution

  • Definitely. You could create multiple instances of the same worker file. This will create a new thread that runs parallel with your main thread per worker. In that worker you should define the functions that do your heavy lifting.

    You can either map your functions on an object so that you can pick the function to execute when sending data to the worker (like in the example below) or create individual worker files that each have their own unique functions.

    Below I've chosen the former. Create multiple instances of your worker and listen to the message event to receive your data.

    Send some instructions to your workers. You could do this in an array to send a list of things that the worker should do and respond with, like what function to call and the arguments to pass.

    // app.js
    
    const workerOne = new Worker('worker.js');
    const workerTwo = new Worker('worker.js');
    
    workerOne.addEventListener('message', ({ data }) => {
      console.log(data); // Received a result!
    });
    
    workerTwo.addEventListener('message', ({ data }) => {
      console.log(data); // Received a result!
    });
    
    workerOne.postMessage([
      {
        action: 'algorithmDefault', // Function to call.
        args: [5, 10] // Arguments to pass to function.
      },
      {
        action: 'arrayToImage',
        args: [['something', 10], 13]
      },
    ]);
    
    workerTwo.postMessage([
      {
        action: 'algorithmDefault',
        args: [2, 20]
      },
      {
        action: 'arrayToImage',
        args: [[2, 90, 'whatever'], 9]
      },
    ]);
    

    In the worker also listen for the message event to receive the instructions from the main thread. Loop through these instructions and call the appropriate action with the arguments that are provided. You could send back all results immediately or collect them in an array and send it back after the loop has completed.

    // worker.js
    
    const actions = {
      algorithmDefault: (division, colorVal) => {
         // ... return something.
      },
      arrayToImage: (increment, colorVal) => {
         // ... return something.
      }
    };
    
    self.addEventListener('message', ({ data }) => {
      const results = [];
      for (const { action, args } of data) {
        const actionToCall = actions[action]; // Get the function to call.
        const resultOfAction = actionToCall(...args); // Calculating...
        results.push(resultOfAction); // Collect the result.
      }
      postMessage(resultOfAction); // Send back the results.
    });