【问题标题】:IndexedDB's callbacks not being executed inside the 'fetch' event of a Service WorkerIndexedDB 的回调没有在 Service Worker 的 'fetch' 事件中执行
【发布时间】:2023-04-01 21:57:01
【问题描述】:

当应用程序向服务器请求新页面时,我试图在 Service Worker 的“获取”事件中的 IndexedDB 数据库中做几件事。这就是我的目标:

  1. 创建一个新的对象存储(它们需要根据 'fetch' 获取的数据动态创建);
  2. 在商店中存储一个元素。

或者,如果商店已经存在:

  1. 从商店中获取元素;
  2. 更新元素并将其存储回商店。

问题是回调(onupgradeneeded、onsuccess 等)永远不会被执行。

我一直在尝试使用彼此内部的回调,尽管我知道这可能不是最好的方法。我也尝试在“获取”上放置event.waitUntil(),但没有帮助。

'fetch' 事件,其中函数 registerPageAccess 被调用:

self.addEventListener('fetch', function (event) {
    event.respondWith(
        caches.match(event.request)
            .then(function (response) {                 
                event.waitUntil(function () {
                    const nextPageURL = new URL(event.request.url);
                    if (event.request.destination == 'document') {
                        if (currentURL) {
                            registerPageAccess(currentURL, nextPageURL);
                        }
                        currentURL = nextPageURL;
                    }
                }());

                /* 
                *    some other operations
                */
                return response || fetch(event.request);
            })
    );
});

registerPageAccess,带有回调的函数。 我知道代码很多,但请看第 5 行的secondRequest.onupgradeneeded。它永远不会被执行,更不用说下面的了。

function registerPageAccess(currentPageURL, nextPageURL) {

    var newVersion = parseInt(db.version) + 1;
    var secondRequest = indexedDB.open(DB_NAME, newVersion);
    secondRequest.onupgradeneeded = function (e) {

        db = e.target.result;
        db.createObjectStore(currentPageURL, { keyPath: "pageURL" });
        var transaction = request.result.transaction([currentPageURL], 'readwrite');
        var store = transaction.objectStore(currentPageURL);
        var getRequest = store.get(nextPageURL);
        getRequest.onsuccess = function (event) {

            var obj = getRequest.result;

            if (!obj) {
                // Insert element into the database
                console.debug('ServiceWorker: No matching object in the database');
                const addRes = putInObjectStore(nextPageURL, 1, store);
                addRes.onsuccess = function (event) {

                    console.debug('ServiceWorker: Element was successfully added in the Object Store');

                }
                addRes.onerror = function (event) {
                    console.error('ServiceWorker error adding element to the Object Store: ' + addRes.error);
                }
            }
            else {
                // Updating database element
                const updRes = putInObjectStore(obj.pageURL, obj.nVisits + 1, store);
                updRes.onsuccess = function (event) {

                    console.debug('ServiceWorker: Element was successfully updated in the Object Store');

                }
                updRes.onerror = function (event) {
                    console.error('ServiceWorker error updating element of the Object Store: ' + putRes.error);
                }
            }
        };
    };
    secondRequest.onsuccess = function (e) {
        console.log('ServiceWorker: secondRequest onsuccess');
    };
    secondRequest.onerror = function (e) {
        console.error('ServiceWorker: error on the secondRequest.open: ' + secondRequest.error);
    };
}

我需要一种方法来执行registerPageAccess 中的操作,这涉及执行几个回调,但浏览器似乎在它们发生之前杀死了 Service Worker。

【问题讨论】:

  • 您是在等待函数调用完成后再响应吗?
  • 由于'fetch'响应不依赖于函数registerPageAccess,我只想最终被完全执行。

标签: javascript service-worker indexeddb


【解决方案1】:

Service Worker 内部的所有异步逻辑都需要基于 Promise。因为 IndexedDB 是基于回调的,所以您会发现自己需要将相关的回调包装在一个 Promise 中。

我强烈建议不要尝试自己进行此操作,而是使用以下经过充分测试、高效且轻量级的库之一:

  • idb-keyval,如果您对简单的键值存储没问题。
  • idb 如果您需要完整的 IndexedDB API。

我还建议您考虑在 Service Worker 的 fetch 处理程序中使用 async/await syntax,因为它会使基于 Promise 的代码更具可读性。

综合起来,大致如下:

self.addEventListener('fetch', (event) => {
  event.waitUntil((async () => {
    // Your IDB cleanup logic here.
    // Basically, anything that can execute separately
    // from response generation.
  })());

  event.respondWith((async () => {
    // Your response generation logic here.
    // Return a Response object at the end of the function.
  })());
});

【讨论】:

  • 非常感谢您清晰快速的回答!我现在使用 idb 和你推荐的结构,遗憾的是我得到 localhost:64228”的 FetchEvent 导致网络错误响应:一个不是响应的对象被传递给 respondWith() 。我想我到处都在返回 Response 对象。也许我必须发布另一个问题......非常感谢,@jeff
猜你喜欢
  • 2018-07-19
  • 1970-01-01
  • 1970-01-01
  • 2018-02-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多