【问题标题】:Webworker return result after callbackWebworker回调后返回结果
【发布时间】:2016-02-05 01:13:54
【问题描述】:

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

我替换了代码以向我的新 Web Worker 脚本发布消息。 webworker 的回调在我的initWorkers 函数中,我在其中添加了事件监听器。

我的目标是 completeClass 函数返回 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);
};

【问题讨论】:

  • 你的意思是你想为结果返回一个承诺?如果您不异步执行此操作,则使用 webworker 将没有任何意义。
  • 所有workerdefertext 在您的initWorkers 方法中都未定义,您也不能在事件回调中return
  • @Bergi 问题是“CompleteClass”函数太占用 CPU,所以我将它移到了 webworker 上,以便它在单独的线程上运行。完成后,我想返回结果。所以是的,它异步运行。我对所有关于 'defer' 、 'promise' 和 'resolve' 的术语有点迷茫,所以我不确定我需要什么。
  • @Bergi 我曾经在我的 completeClass 函数中有 addEventListener,但是每次我调用该函数时它都会添加监听器
  • 要多次调用函数吗?甚至多次,而之前的结果可能还没有返回?那么无论如何它会变得更加复杂。

标签: javascript angularjs asynchronous promise web-worker


【解决方案1】:

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

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

  • 为每个请求生成一个唯一的 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);
};

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

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

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

注意:从技术上讲,如果工作人员在一个请求上同步完成所有工作,并且按照接收到的顺序响应每个调用,则不需要在每个请求中传递一个 ID。在上面的例子中,主线程总是可以假设对 worker 的调用构成一个先进先出队列。然而,传递一个 ID 给了工人不按收到的顺序完成工作的灵活性。说在以后的版本中它需要做一些异步的事情,比如调用另一个工人,或者发出一个 ajax 请求,这个方法将允许这样做。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多