简体   繁体   English

服务工作者在从另一个工作人员获取数据后响应获取

[英]Service Worker Respond To Fetch after getting data from another worker

I am using service workers to intercept requests for me and provide the responses to the fetch requests by communicating with a Web worker (also created from the same parent page). 我正在使用服务工作者拦截对我的请求,并通过与Web工作者(也是从同一父页面创建)进行通信来提供对获取请求的响应。 I have used message channels for direct communication between the worker and service worker. 我使用消息通道在工作者和服务工作者之间进行直接通信。 Here is a simple POC I have written: 这是我写的一个简单的POC:

var otherPort, parentPort;
var dummyObj;

var DummyHandler = function()
{
    this.onmessage = null;
    var selfRef = this;

    this.callHandler = function(arg)
    {
        if (typeof selfRef.onmessage === "function")
        {
            selfRef.onmessage(arg);
        }
        else
        {
            console.error("Message Handler not set");
        }
    };
};

function msgFromW(evt)
{
    console.log(evt.data);
    dummyObj.callHandler(evt);
}

self.addEventListener("message", function(evt) {
    var data = evt.data;
    if(data.msg === "connect")
    {
        otherPort = evt.ports[1];
        otherPort.onmessage = msgFromW;
        parentPort = evt.ports[0];
        parentPort.postMessage({"msg": "connect"});
    }
});

self.addEventListener("fetch", function(event)
{
    var url = event.request.url;
    var urlObj = new URL(url);
    if(!isToBeIntercepted(url))
    {
        return fetch(event.request);
    }
    url = decodeURI(url);

    var key = processURL(url).toLowerCase();
    console.log("Fetch For: " + key);

    event.respondWith(new Promise(function(resolve, reject){
        dummyObj = new DummyHandler();
        dummyObj.onmessage = function(e)
        {
            if(e.data.error)
            {
                reject(e.data.error);
            }
            else
            {
                var content = e.data.data;
                var blob = new Blob([content]);
                resolve(new Response(blob));
            }
        };

        otherPort.postMessage({"msg":  "content", param: key});
    }));
});

Roles of the ports: 港口的角色:

otherPort : Communication with worker otherPort :与工人沟通

parentPort : Communication with parent page parentPort :与父页面的通信

In the worker, I have a database say this: 在工人中,我有一个数据库说:

var dataBase = {
    "file1.txt": "This is File1",
    "file2.txt": "This is File2"
};

The worker just serves the correct data according to the key sent by the service worker. 工作人员只根据服务工作人员发送的密钥提供正确的数据。 In reality these will be very large files. 实际上这些将是非常大的文件。

The problem I am facing with this is the following: 我面临的问题如下:

  1. Since I am using a global dummyObj, the older dummyObj and hence the older onmessage is lost and only the latest resource is responded with the received data. 由于我使用的是全局dummyObj,因此较旧的dummyObj以及较旧的onmessage丢失,并且仅使用接收的数据响应最新的资源。
  2. In fact, file2 gets This is File1 , because the latest dummyObj is for file2.txt but the worker first sends data for file1.txt. 实际上,file2获取This is File1 File1,因为最新的dummyObj用于file2.txt,但是worker首先发送file1.txt的数据。

I tried by creating an iframe directly and all the requests inside it are intercepted: 我尝试直接创建iframe,并拦截其中的所有请求:

<html>
<head></head>
<body><iframe src="tointercept/file1.txt" ></iframe><iframe src="tointercept/file2.txt"></iframe>
</body>
</html>

Here is what I get as output: 这是我得到的输出: 在此输入图像描述

One approach could be to write all the files that could be fetched into IndexedDB in the worker before creating the iframe. 一种方法是在创建iframe之前编写可以在worker中的IndexedDB中获取的所有文件。 Then in the Service Worker fetch those from indexed DB. 然后在Service Worker中从索引的DB中获取它们。 But I don't want to save all the resources in IDB. 但我不想保存IDB中的所有资源。 So this approach is not what I want. 所以这种方法不是我想要的。

Does anybody know a way to accomplish what I am trying to do in some other way? 有没有人知道如何以其他方式完成我想要做的事情? Or is there a fix to what I am doing. 或者我正在做什么修复。

Please Help! 请帮忙!

UPDATE UPDATE

I have got this to work by queuing the dummyObjs in a global queue instead of having a global object. 我通过在一个全局队列中排队dummyObjs而不是一个全局对象来实现这一点。 And on receiving the response from the worker in msgFromW I pop an element from the queue and call its callHandler function. 在收到msgFromW工作者的响应后,我从队列中弹出一个元素并调用其callHandler函数。 But I am not sure if this is a reliable solution. 但我不确定这是否是一个可靠的解决方案。 As it assumes that everything will occur in order. 因为它假设一切都会按顺序发生。 Is this assumption correct? 这个假设是否正确?

I'd recommend wrapping your message passing between the service worker and the web worker in promises, and then pass a promise that resolves with the data from the web worker to fetchEvent.respondWith() . 我建议在promises中将服务工作者和Web worker之间的消息包装起来,然后将一个使用Web worker的数据解析的promise传递给fetchEvent.respondWith()

The promise-worker library can automate this promise-wrapping for you, or you could do it by hand, using this example as a guide. promise-worker库可以为您自动执行此承诺包装,或者您可以手动执行此操作,使用此示例作为指南。

If you were using promise-worker , your code would look something like: 如果您使用的是promise-worker ,那么您的代码将类似于:

var promiseWorker = new PromiseWorker(/* your web worker */);

self.addEventListener('fetch', function(fetchEvent) {
  if (/* some optional check to see if you want to handle this event */) {
    fetchEvent.respondWith(promiseWorker.postMessage(/* file name */));
  }
});

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM