在 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 已经不在了。
在 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 已经不在了。