Skip to content Skip to sidebar Skip to footer

Webworker Return Result After Callback

I have an Angular service where I'm using $q service in combination with webworkers. In my original function before using webworkers, my completeClass function would return an obje

Solution 1:

If you are going to call the worker when the previous call is still running, then you need something to either queue or keep track of in-progress requests. My suspicion is that unless you need control of the queue of requests, it's simpler for UI thread to fire off the requests to the worker, so the browser essentially queues the requests for you.

But you would still need to keep track of the requests sent somehow, so when you get a message back from the worker, you know which one it is responding do. You can do this by, in the main thread

  • Generating a unique ID for each request. An ever-increasing integer can be enough for a lot of cases.
  • Creating a deferred object and storing it, associated with the ID
  • Firing off the request to the worker, passing the ID
  • Passing a the promise of the deferred object back to the caller

The worker then

  • Receives the message, with the ID
  • Does its' work
  • Posts the result back, along with the ID

The main thread then

  • Receives the message, with the ID and result of the work.
  • Retrieves the deferred object by the ID, and resolves it with the results of the work.

To do this, you can use some code like below. I've slightly simplified your case by passing just text to the worker, getting completeText back. You can add more information going either way in a real case.

app.service('AutoComplete', function($q) {
  var id = 0;
  var worker = newWorker('app/shared/autocomplete/autocomplete-class-worker.js');
  var inProgress = {};

  this.completeClass = function(text) {
    var deferred = $q.defer();
    id++;
    var request = {
      id: id,
      text: text
    };
    inProgress[id] = deferred;
    worker.postMessage(request);
    return deferred.promise;
  };

  worker.onmessage = function(e) {
    var response = e.data;
    var id = response.id;
    vartype = response.type; // resolve, reject, or notifyvar completeText = response.completeText;
    inProgress[id][type](completeText);
    if (type === 'resolve' || type === 'reject') {
      delete inProgress[id];
    }
  };
});

Then in the worker you can have code like:

self.onmessage = function(e) {
  var request = e.data;
  var text = request.text;
  var id = request.id;

  // Do the work herevar completeText = ...

  var response = {
    id: id,
    type: 'resolve', // Can reject the promise in the main thread if desired
    completeText: completeText
  };

  self.postMessage(response);
};

My goal is that the completeClass function returns the result of the webworker. How can I make this happen?

To clarify, it can't directly return the result, because the result is calculated asynchronously, and all function calls must return synchronously. But it can return a promise that resolves to the result later, just like $http does for example. To use the above, you can do something like

app.controller('MyController', function($scope, AutoComplete) {
   $scope.complete = function(text) {
     AutoComplete.completeClass(text).then(function(result) {
       // Do something with result
     });
   });
});

Note: technically, passing an ID along with each request isn't necessary if the worker does all its' work synchronously on one request, and so responds to each call in the order received. In the above example, the main thread can always assume the calls to the worker make a first-in-first-out queue. However, passing an ID gives the flexibility of the worker not finishing the work in the order received. Say in a later version it needs to do something asynchronous, like call another worker, or make an ajax request, this method will allow that.

Post a Comment for "Webworker Return Result After Callback"