Angular 服务和网络工作者

Angular Service and Web Workers

我有一个 Angular 1 应用程序,我正在尝试提高进行大量计算的特定服务的性能(并且可能没有优化,但现在除此之外,运行 它在另一个线程中是现在提高动画性能的目标)

应用程序

该应用程序对您的 GPA、学期、课程作业等进行计算。服务名称是 calc。在 Calc 中有 usertermcourseassign 命名空间。每个命名空间都是一个对象,格式如下

{
    //Times for the calculations (for development only)
    times:{
        //an array of calculation times for logging and average calculation
        array: []

        //Print out the min, max average and total calculation times
        report: function(){...}
    },

    //Hashes the object (with service.hash()) and checks to see if we have cached calculations for the item, if not calls runAllCalculations()
    refresh: function(item){...},

    //Runs calculations, saves it in the cache (service.calculations array) and returns the calculation object
    runAllCalculations: function(item){...}
}

这是来自 IntelliJ 非常好的结构选项卡的屏幕截图,有助于可视化

需要做什么?

  1. 检测 Web Worker 兼容性 (MDN)
  2. 根据 Web Worker 兼容性构​​建服务

    一个。 结构与现在完全相同

    b。替换为 Web Worker "proxy"(术语正确?)service

问题

问题是如何创建 Web Worker "Proxy" 以保持与其余代码相同的服务行为。

Requirements/Wants

我想要的一些东西:

问题

...我想到目前为止还没有真正的问题,这只是对问题的解释...所以事不宜迟...

使用 Angular 服务工厂 检测 Web Worker 兼容性的最佳方法是什么,有条件地 将服务实现为 Web Worker,同时保持相同的服务行为,保持DRY代码维护支持 Web Worker 兼容的浏览器?

其他注意事项

我也看过VKThread,这可能对我的情况有所帮助,但我不确定如何最好地实施它。

更多资源:

一般来说,制作可在 worker 中运行的可管理代码的好方法 - 尤其是在同一 window 中也可以 运行 的代码(例如,当不支持 worker 时)是使代码成为事件驱动的,然后使用简单的代理通过通信渠道驱动事件——在本例中为 worker。

我首先创建了抽象 "class",它并没有真正定义向另一方发送事件的方式。

   function EventProxy() {
     // Object that will receive events that come from the other side
     this.eventSink = null;
     // This is just a trick I learned to simulate real OOP for methods that
     // are used as callbacks
     // It also gives you refference to remove callback
     this.eventFromObject = this.eventFromObject.bind(this);
   }
   // Object get this as all events callback
   // typically, you will extract event parameters from "arguments" variable 
   EventProxy.prototype.eventFromObject = (name)=>{
     // This is not implemented. We should have WorkerProxy inherited class.
     throw new Error("This is abstract method. Object dispatched an event "+
                     "but this class doesn't do anything with events.";
   }

   EventProxy.prototype.setObject = (object)=> {
     // If object is already set, remove event listener from old object
     if(this.eventSink!=null)
       //do it depending on your framework
       ... something ...
     this.eventSink = object;
     // Listen on all events. Obviously, your event framework must support this
     object.addListener("*", this.eventFromObject);
   }
   // Child classes will call this when they receive 
   // events from other side (eg. worker)
   EventProxy.prototype.eventReceived = (name, args)=> {
     // put event name as first parameter
     args.unshift(name);
     // Run the event on the object
     this.eventSink.dispatchEvent.apply(this.eventSink, args);
   }

然后你为工人实现这个,例如:

   function WorkerProxy(worker) {
     // call superconstructor
     EventProxy.call(this);
     // worker
     this.worker = worker;
     worker.addEventListener("message", this.eventFromWorker = this.eventFromWorker.bind(this));
   }

   WorkerProxy.prototype = Object.create(EventProxy.prototype);
   // Object get this as all events callback
   // typically, you will extract event parameters from "arguments" variable 
   EventProxy.prototype.eventFromObject = (name)=>{
       // include event args but skip the first one, the name
       var args = [];
       args.push.apply(args, arguments);
       args.splice(0, 1);
       // Send the event to the script in worker
       // You could use additional parameter to use different proxies for different objects
       this.worker.postMessage({type: "proxyEvent", name:name, arguments:args});
   }
   EventProxy.prototype.eventFromWorker = (event)=>{
       if(event.data.type=="proxyEvent") {
           // Use superclass method to handle the event
           this.eventReceived(event.data.name, event.data.arguments);
       }
   }

那么用法就是你有一些服务和一些接口,在你做的页面代码中:

// Or other proxy type, eg socket.IO, same window, shared worker...
var proxy = new WorkerProxy(new Worker("runServiceInWorker.js"));
//eg user clicks something to start calculation
var interface = new ProgramInterface(); 
// join them 
proxy.setObject(interface);

而在 runServiceInWorker.js 中你做的几乎一样:

importScripts("myservice.js", "eventproxy.js");
// Here we're of course really lucky that web worker API is symethric
var proxy = new WorkerProxy(self);
// 1. make a service
// 2. assign to proxy
proxy.setObject(new MyService());
// 3. profit ...

根据我的经验,有时我不得不检测我在哪一边,但那是使用不对称的网络套接字(有服务器和许多客户端)。您可能 运行 遇到与共享工作人员类似的问题。

您提到了 Promises - 我认为 promises 的方法是相似的,但可能更复杂,因为您需要将回调存储在某个地方并通过请求的 ID 对它们进行索引。但肯定是可行的,如果你从不同的来源调用辅助函数,可能会更好。

我是问题中提到的vkThread插件的作者。是的,我开发了 Angular version of vkThread plugin,它允许您在单独的线程中执行函数。

函数可以直接在主线程中定义或从外部 javascript 文件中调用。

函数可以是:

  • 常规函数
  • 对象的方法
  • 具有依赖性的函数
  • 具有上下文的函数
  • 匿名函数

基本用法:

/* function to execute in a thread */
function foo(n, m){ 
    return n + m;
}

// to execute this function in a thread: //

/* create an object, which you pass to vkThread as an argument*/
var param = {
      fn: foo      // <-- function to execute
      args: [1, 2] // <-- arguments for this function
    };

/* run thread */
vkThread.exec(param).then(
   function (data) {
       console.log(data);  // <-- thread returns 3 
    }
);

示例和 API 文档:http://www.eslinstructor.net/ng-vkthread/demo/

希望这对您有所帮助,

--瓦迪姆