Webworker return 回调后的结果

Webworker return result after callback

我有一个 Angular 服务,我将 $q 服务与网络工作者结合使用。在我使用 webworkers 之前的原始函数中,我的 completeClass 函数将 return 一个对象。

我将代码替换为 post 一条消息到我的新网络工作者脚本。 Webworker 的回调在我添加事件监听器的 initWorkers 函数中。

我的目标是 completeClass 函数 return 是 webworker 的结果。我怎样才能做到这一点?

this.classWorker = new Worker('app/shared/autocomplete/autocomplete-class-worker.js');

this.completeClass = function(text){
    var self = this;    
    var defer = $q.defer();
    classWorker.postMessage([text, this.oldText, this.oldProposals, this.aliases, this.entityClasses])
};

this.initWorkers = function(){
    var self = this;
    worker.addEventListener('message', function(e) {
        defer.resolve(e.data);
        self.oldProposals = e.data[1];
        self.oldText = text;
        return e.data[0];
    }, false);
};

如果您要在上一个调用仍在 运行 时调用工作人员,那么您需要一些东西来排队或跟踪正在进行的请求。我的怀疑是,除非您需要控制请求队列,否则 UI 线程向 worker 发出请求会更简单,因此浏览器基本上会为您排队请求。

但是您仍然需要跟踪以某种方式发送的请求,因此当您从工作人员那里收到一条消息时,您知道它正在响应哪个请求。您可以通过在主线程中执行此操作

  • 为每个请求生成一个唯一的 ID。对于很多情况,一个不断增加的整数就足够了。
  • 创建延迟对象并存储它,并与 ID
  • 相关联
  • 向工作人员发出请求,传递 ID
  • 将延迟对象的承诺传递回调用者

当时的工人

  • 收到消息,ID
  • 它的工作
  • 返回结果,连同 ID

然后是主线程

  • 收到消息,包含工作的 ID 和结果。
  • 通过ID检索延迟的对象,并用工作结果解析它。

为此,您可以使用如下代码。我通过仅将 text 传递给工作人员并返回 completeText 来稍微简化您的情况。您可以在真实案例中添加更多信息。

app.service('AutoComplete', function($q) {
  var id = 0;
  var worker = new Worker('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;
    var type = response.type; // resolve, reject, or notify
    var completeText = response.completeText;
    inProgress[id][type](completeText);
    if (type === 'resolve' || type === 'reject') {
      delete inProgress[id];
    }
  };
});

然后在 worker 中你可以有这样的代码:

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

  // Do the work here
  var 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?

说明一下,不能直接return结果,因为结果是异步计算的,所有的函数调用都必须return同步。但它可以 return 一个稍后解析为结果的承诺,就像 $http 所做的那样。要使用上面的内容,您可以执行类似

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

注意:从技术上讲,如果 worker 对一个请求同步完成所有工作,则没有必要将 ID 与每个请求一起传递,因此按收到的顺序响应每个调用。在上面的示例中,主线程始终可以假定对工作线程的调用构成一个先进先出队列。但是,传递 ID 可以让工作人员灵活地不按收到的订单完成工作。比如说在以后的版本中它需要做一些异步的事情,比如调用另一个工作人员,或者发出一个 ajax 请求,这个方法将允许这样做。