IndexedDB 的回调未在 Service Worker 的 'fetch' 事件中执行

IndexedDB's callbacks not being executed inside the 'fetch' event of a Service Worker

当应用程序向服务器请求新页面时,我正在尝试在服务工作者的 'fetch' 事件中的 IndexedDB 数据库中做几件事。这就是我的目标:

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

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

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

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

我一直在尝试使用彼此内部的回调,但我知道这可能不是最好的方法。我也试过在 'fetch' 上放置一个 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。

Service Worker 内部的所有异步逻辑都需要基于承诺。因为 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.
  })());
});