Сервисный работник отвечает на выборку после получения данных от другого работника

Я использую сервисных работников для перехвата запросов и предоставления ответов на запросы выборки, общаясь с веб-работником (также созданным с той же родительской страницы). Я использовал каналы сообщений для прямой связи между работником и служащим. Вот простой 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});
    }));
});

Роли портов:

otherPort: общение с работником

parentPort: связь с родительской страницей

У работника, у меня есть база данных, это сказать:

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

Работник просто передает правильные данные в соответствии с ключом, отправленным работником службы. На самом деле это будут очень большие файлы.

Проблема, с которой я сталкиваюсь, заключается в следующем:

  1. Поскольку я использую глобальный dummyObj, старый dummyObj и, следовательно, более старый onmessage теряются, и только последний ресурс отвечает полученными данными.
  2. На самом деле, file2 получает This is File1потому что последний dummyObj предназначен для file2.txt, но рабочий сначала отправляет данные для file1.txt.

Я попытался создать iframe напрямую, и все запросы внутри него перехватываются:

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

Вот что я получаю в качестве вывода:

Одним из подходов может быть запись всех файлов, которые могут быть извлечены в IndexedDB на рабочем месте перед созданием iframe. Затем в Service Worker извлеките их из проиндексированной БД. Но я не хочу сохранять все ресурсы в IDB. Так что такой подход не то, что я хочу.

Кто-нибудь знает способ выполнить то, что я пытаюсь сделать каким-либо другим способом? Или есть исправление того, что я делаю.

Пожалуйста помоги!

ОБНОВИТЬ

Я заставил это работать, помещая dummyObjs в глобальную очередь вместо того, чтобы иметь глобальный объект. И при получении ответа от работника в msgFromW Я выталкиваю элемент из очереди и вызываю его callHandler функция. Но я не уверен, что это надежное решение. Как предполагается, что все будет происходить по порядку. Это предположение верно?

1 ответ

Я бы порекомендовал обернуть ваше сообщение, передаваемое между работником службы и веб-работником, в обещания, а затем передать обещание, которое сопоставляется с данными веб-работника, в fetchEvent.respondWith(),

promise-worker библиотека может автоматизировать эту упаковку обещаний для вас, или вы можете сделать это вручную, используя этот пример в качестве руководства.

Если бы вы использовали 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 */));
  }
});
Другие вопросы по тегам