角色服务和Web工作者

时间:2016-02-25 20:02:32

标签: angularjs angularjs-service web-worker

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

The App

该应用运行您的GPA,条款,课程分配等计算。服务名称为calc。 Inside 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非常漂亮的结构选项卡中的截图,以帮助实现可视化

Service Structure

需要做什么?

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

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

    湾替换为Web Worker“代理”(正确的术语?)服务

  3. 问题

    问题是如何创建Web Worker“Proxy”以维护与其余代码相同的服务行为。

    要求/想

    我想要的一些事情:

    • 最重要的是,如上所述,保持服务行为不变
    • 要为服务保留一个代码库,请将其保留为 DRY ,而不必修改两个位置。我已经查看了WebWorkify,但我不确定如何最好地实现它。
    • 在等待工人完成时使用Promise
    • 在工作人员中使用Angular和可能的其他服务(如果可能的话)再次WebWorkify似乎解决了这个问题

    问题

    ...我想到目前为止还没有真正的问题,这只是对问题的解释......所以没有进一步的麻烦......

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

    其他注释

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

    更多资源:

2 个答案:

答案 0 :(得分:2)

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

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

   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);
   }

然后你为worker实现这个例子:

   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索引它们。但肯定可行,如果您从不同的来源调用工作者功能,可能会更好。

答案 1 :(得分:0)

我是问题中提到的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/

希望这有帮助,

- 瓦迪姆