最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

html - javascript:worker synchronization - Stack Overflow

programmeradmin7浏览0评论

I am working on HTML5 web worker and I made a function that spawn few workers and return the result, but problem is that it returns the value before the worker updates the result. So I want to delay return statement until all results are received

for (i = 0; i < array1_rows; i++)
{
    var worker = new Worker('json.js');
    worker.postMessage(arr1[i]);
    worker.postMessage(arr2);
    worker.postMessage(i);

    worker.onmessage = storeResult;
}

/////////////////////////////////

return result;

So I just want to delay that return statement until result are received. Plz help me on how to use the yield in java script.

I am working on HTML5 web worker and I made a function that spawn few workers and return the result, but problem is that it returns the value before the worker updates the result. So I want to delay return statement until all results are received

for (i = 0; i < array1_rows; i++)
{
    var worker = new Worker('json.js');
    worker.postMessage(arr1[i]);
    worker.postMessage(arr2);
    worker.postMessage(i);

    worker.onmessage = storeResult;
}

/////////////////////////////////

return result;

So I just want to delay that return statement until result are received. Plz help me on how to use the yield in java script.

Share Improve this question edited Aug 23, 2013 at 5:47 Purnil Soni 8314 silver badges18 bronze badges asked Aug 23, 2013 at 5:24 vickyvicky 6831 gold badge8 silver badges14 bronze badges 1
  • 1 Web Workers are asynchronous, you can't synchronize them. – bfavaretto Commented Aug 23, 2013 at 5:39
Add a ment  | 

2 Answers 2

Reset to default 7

Like the ment points out - Web Workers work asynchronously (think of how AJAX works)

You can use an asynchronous semaphore to yield only when they're all done

function doSomething(callback){
    var counter = array1_rows;
    for (i = 0; i < array1_rows; i++)
    {
        var worker = new Worker('json.js');
        worker.postMessage(arr1[i]);
        worker.postMessage(arr2);
        worker.postMessage(i);

        worker.onmessage = function(){
            storeResult.apply(arguments);//call it the same way it was called before.
            counter--;
            if(counter === 0){ // all workers returned
                callback(result); //or whatever you're updating in storeResult
            }
        };
    }
}

Now you can call it like:

doSomething(function(result){
    console.log(result); //this will print the correct result.
});

For more information about how JS async operations work I remend this ajax related question that includes a description of the problem and how to approach it.,

Use Promises!

The Promise object represents the eventual pletion (or failure) of an asynchronous operation and its resulting value.


Step 1: Extend the native Worker class

// main.js

class MyWorker extends Worker {

  constructor (src) {
    super(src)
  }
  
}
// worker.js

const COMMAND_ONE = 0
const COMMAND_TWO = 1

addEventListener('message', ({data}) => {
  let result
  
  switch (data.type) {
    case COMMAND_ONE:
      // Some awesome off-main putations
      // result = ...
      break
    case COMMAND_TWO:
      // etc.
      break
    default:
      result = 'Received an empty message')
  }
  
  postMessage(result)
})

Step 2: Override postMessage()

By overriding the native postMessage() method, not only can we send messages to the worker thread, we can send the result back in a Promise. In the Worker script, no changes need to be made (yet).

// main.js

class MyWorker extends Worker {

  constructor (src) {
    super(src)
  }
  
  postMessage (message, transfer=[]) {
    return new Promise((resolve) => {
      const onmessage = ({data}) => {
        this.removeEventListener('message', onmessage)
        resolve(data)
      }
      this.addEventListener('message', onmessage)
      super.postMessage(obj, transfer)
    })
  }
  
}

Step 3: Use MessagePorts

This is not optional. Since webworkers are asynchronous with the main thread, we cannot simply assume that any of MyWorker's responses will be received in the same order of the calls to postMessage().

For correct asynchronous handling we need to guarantee that the message we received in our local onmessage arrow function is indeed a response to the respective postMessage() call (rather then any message the Worker thread happened to send back at that time).

// main.js

class MyWorker extends Worker {

  constructor (src) {
    super(src)
  }
  
  postMessage (obj, transfer=[]) {
    return new Promise((resolve) => {
      const {port1, port2} = new MessageChannel()
      transfer.push(port2)
      this._lastCall = Date.now()
      const onmessage = ({data}) => {
        port1.removeEventListener('message', onmessage)
        resolve(data)
      }
      port1.addEventListener('message', onmessage)
      super.postMessage(obj, transfer)
      port1.start()
    })
  }
  
}
// worker.js

const COMMAND_ONE = 0
const COMMAND_TWO = 1

addEventListener('message', ({data, ports}) => {
  const port = ports[0]
  let result
  
  switch (data.type) {
    case COMMAND_ONE:
      // Some awesome off-main putations
      // result = ...
      break
    case COMMAND_TWO:
      // etc.
      break
    default:
      result = 'Received an empty message')
  }
  
  // Send result back via a unique Port
  port.postMessage(result)
})

Step 4: Use async/await

Rather then just calling postMessage() (which is still valid and could be appropriate e.g. if no response is expected and the timing the Worker script has no impact on other code) we can now use async functions and await the result before we move on.

Be careful to only use await if you are sure you will receive a response from the Worker thread. Not doing so will lock the async function!

// main.js

class MyWorker {
  
  static COMMAND_ONE = 0
  static COMMAND_TWO = 1
  // etc.
  
  // ...
}

async function foo (w) {
  let value1 = await w.postMessage({type: MyWorker.COMMAND_ONE, values: ''})
  value1 += value1 + await w.postMessage({type: MyWorker.COMMAND_TWO, values: ''})
  
  let raceFor = []
  raceFor.push(w.postMessage({type: MyWorker.COMMAND_THREE, values: ''}))
  raceFor.push(w.postMessage({type: MyWorker.COMMAND_FOUR, values: ''}))
  raceFor.push(w.postMessage({type: MyWorker.COMMAND_FIVE, values: ''}))
  raceFor.push(w.postMessage({type: MyWorker.COMMAND_SIX, values: ''}))
  
  let result
  try {
    result = Promise.race(raceFor)
  }
  catch (e) {
    result = Promise.resolve('No winners')
  }
  
  return result
}

const w1 = new MyWorker('worker.js')
const res = foo(w1)

Promise - Javascript | MDN

MessageChannel - Javascript | MDN

async - Javascript | MDN

发布评论

评论列表(0)

  1. 暂无评论