在 ServiceWorker 中访问 indexedDB。竞争条件

Accessing indexedDB in ServiceWorker. Race condition

在 ServiceWorker 中演示 indexedDB 的例子还不多,但我看到的都是这样结构的:

const request = indexedDB.open( 'myDB', 1 );
var db;

request.onupgradeneeded = ...

request.onsuccess = function() {
    db = this.result; // Average 8ms
};


self.onfetch = function(e)
{
    const requestURL = new URL( e.request.url ),
    path = requestURL.pathname;

    if( path === '/test' )
    {
        const response = new Promise( function( resolve )
        {
            console.log( performance.now(), typeof db ); // Average 15ms

            db.transaction( 'cache' ).objectStore( 'cache' ).get( 'test' ).onsuccess = function()
            {
                resolve( new Response( this.result, { headers: { 'content-type':'text/plain' } } ) );
            }
        });

        e.respondWith( response );
    }
}

这是否可能在 ServiceWorker 启动时失败,如果是这样,在 ServiceWorker 中访问 indexedDB 的可靠方法是什么?

我不知道通过受控页面访问 IndexedDB 从 service worker 上下文访问 IndexedDB 有什么特别之处。

Promises 显然让你在 service worker 中的生活更轻松,所以我发现使用 https://gist.github.com/inexorabletash/c8069c042b734519680c 之类的东西比原始的 IndexedDB API 更有用。但它不是强制性的,只要您创建和管理自己的承诺以反映异步 IndexedDB 操作的状态。

编写 fetch 事件处理程序时要牢记的主要事情(这不是特定于使用 IndexedDB),如果调用 event.respondWith(),则需要传入Response 对象或使用 Response 对象解析的承诺。只要你这样做,你的 Response 是从 IndexedDB 条目还是缓存 API 或其他地方构造的都无关紧要。

您 运行 是否发现您发布的代码存在任何实际问题,或者这更像是一个理论问题?

您可以像这样在 promise 中包装交易:

var tx = db.transaction(scope, mode);
var p = new Promise(function(resolve, reject) {
  tx.onabort = function() { reject(tx.error); };
  tx.oncomplete = function() { resolve(); };
});

现在p将resolve/reject交易时completes/aborts。因此,您可以在 tx 交易中执行任意逻辑,并且 p.then(...) and/or 将依赖承诺传递给 e.respondWith()e.waitUntil()

正如其他评论者所指出的,我们确实需要承诺 IndexedDB。但是它的 post 任务自动提交模型和 Promises 使用的微任务队列的组合使得它......在没有基本上完全取代 API 的情况下这样做并不平凡。但是(作为实施者和规范编辑之一)我正在积极制作一些想法的原型。

每次 ServiceWorker 启动时都打开 IDB 不太可能是最佳选择,即使不使用它,您最终也会打开它。相反,在需要时打开数据库。单例在这里非常有用(参见 https://github.com/jakearchibald/svgomg/blob/master/src/js/utils/storage.js#L5),因此如果 IDB 在其生命周期中被使用两次,则无需打开两次。

"activate" 事件是打开 IDB 并允许任何 "onupdateneeded" 事件 运行 的好地方,因为旧版本的 ServiceWorker 已经不在了。