IndexedDB:调用 onUpgradeNeeded 时从 JSON 文件加载数据
IndexedDB: Loading data from a JSON file when onUpgradeNeeded is called
我的网站有一个 JSON 文件,其中包含要传输到我用户的本地 IndexedDB 中的数据。我正在寻找一种仅在实际需要更新时加载此 JSON 文件的方法。
澄清一下,我计划让我的网站 运行 尽可能完全脱离用户本地存储的数据,类似于应用程序。当有新的 IDB 更新可用时,他们应该只需要下载 JSON 文件。
到目前为止,我已经尝试通过 运行 将 onUpgradeNeeded
事件作为异步函数来实现。
if (!window.indexedDB) {
window.alert("Your browser doesn't support a stable version of IndexedDB, which is required for most functions of this website. For the best support, please use the latest version of Chrome, Firefox, or Safari.");
}
else {
var dbVer = 39; //IDB Version (int only)
var recipeObject; //instantiate global variable for module object import
var recipeArray = []; //instantiate global array for module import
var recipeDBver; //instantiate global variable for actual database version (TODO: implement version checking)
var upgradeNeeded = false;
var clearNeeded = false;
var openRequest = indexedDB.open('recipeDatabase', dbVer);
console.log("IDB.js running");
openRequest.onsuccess = function(e) {
console.log('Running onSuccess');
};
openRequest.onerror = function(e) {
console.log('Open Request ERROR');
console.dir(e);
};
openRequest.onupgradeneeded = async function(e) {
var db = e.target.result;
console.log('Running onUpgradeNeeded');
db.onerror = function(errorEvent) {
console.log("onUpgradeNeeded ERROR");
return;
};
importObject = await import("/resources/recipeDB.js");
//TODO: remove debugging
console.log('Module loaded');
console.log(importObject);
recipeObject = importObject.default;
console.log(recipeObject);
recipeDBver = recipeObject.recipeDBver;
console.log(recipeDBver);
recipeArray = recipeObject.recipeArray;
console.log(recipeArray);
upgradeNeeded = true;
if (!db.objectStoreNames.contains('drinks')) {
var storeOS = db.createObjectStore('drinks', {keyPath: 'id'});
storeOS.createIndex('name', 'name', { unique: false });
storeOS.createIndex('type', 'type', { unique: false });
storeOS.createIndex('subtype', 'subtype', { unique: false });
storeOS.createIndex('tags', 'tags', { unique: false });
}
else {
clearNeeded = true;
}
console.log('IDB Upgrade Needed: ' + upgradeNeeded);
console.log('IDB Clear Needed: ' + clearNeeded);
db = e.target.result;
if (clearNeeded == true) {
clearData();
}
else if (upgradeNeeded == true) {
for (var i = 0; i < recipeArray.length; i++) {
addItem(recipeArray[i]);
}
}
};
function clearData() {
var db = openRequest.result;
var transaction = db.transaction(["drinks"], "readwrite");
var objectStore = transaction.objectStore("drinks");
var objectStoreRequest = objectStore.clear();
objectStoreRequest.onerror = function(e) {
console.log('Error clearing data. ', e.target.error.name);
console.dir(e);
};
objectStoreRequest.onsuccess = function(e) {
console.log('Data cleared successfully.')
for (var i = 0; i < recipeArray.length; i++) {
addItem(recipeArray[i]);
}
};
}
function addItem(curItem) {
var db = openRequest.result;
var transaction = db.transaction(['drinks'], 'readwrite');
var store = transaction.objectStore('drinks');
var item = curItem;
var request = store.add(item);
request.onerror = function(e) {
console.log('Error', e.target.error.name);
console.dir(e);
};
request.onsuccess = function(e) {
console.log('Item added: ' + curItem.name);
};
}
}
控制台returns如下:
我假设 onUpgradeNeeded
事件在 JSON 文件能够加载之前超时。
有没有办法延迟超时?如果没有,有没有人知道更好的方法来完成我想做的事情?
onupgradeneeded 事件处理程序需要同步完成。更准确地说,请求 upon/within 版本更改事务 运行 需要在与版本更改事务启动时相同的事件循环滴答中启动。
从你的问题中不清楚,但看起来你正在进行异步调用以加载 json 并等待它加载,而这种等待就是让 versionchange 事务完成的原因,并导致之后发出的所有请求不会在事件循环的同一刻度中发生。
问题从你在控制台看到的就可以看出来。首先我们得到 IDB.js is running
然后你进入你的 onupgradeneeded
处理程序,但是随后,我们没有从那个函数控制台记录任何东西,而是立即看到 onsuccess
处理程序 运行。这是因为您将 onupgradeneeded
处理程序定义为 async
,这意味着该函数在等待导入解析时实质上在 await import("/resources/recipeDB.js");
行停止执行。这实质上意味着就 IDB 事件而言,onupgradeneeded
已完成,需要进入 onsuccess
。正如 Josh 上面所说,这是因为 onupgradeneeded
需要同步解析 。
要解决这个问题,您可以做的是:
- 使
onupgradeneeded
同步
- 更新您的 IDB 架构(创建新的对象存储和索引)
- 导入数据并在成功导入后将它们插入您的数据库
这是使用 IndexedDB 的困难之一:它不是 promised-based,因此使用 promised-based async
函数并不总是能很好地使用它。我通常发现这些 short-comings 需要更多代码来处理它们(例如使用 .then()
调用以便我可以在进行必要的异步活动的同时拥有同步事件处理程序)。
第一步:使用javascript封装json数据。
第二步:在升级事件中使用importScript()
。
第 3 步:运行 你的 indexedDB 脚本 Worker
.
我的网站有一个 JSON 文件,其中包含要传输到我用户的本地 IndexedDB 中的数据。我正在寻找一种仅在实际需要更新时加载此 JSON 文件的方法。
澄清一下,我计划让我的网站 运行 尽可能完全脱离用户本地存储的数据,类似于应用程序。当有新的 IDB 更新可用时,他们应该只需要下载 JSON 文件。
到目前为止,我已经尝试通过 运行 将 onUpgradeNeeded
事件作为异步函数来实现。
if (!window.indexedDB) {
window.alert("Your browser doesn't support a stable version of IndexedDB, which is required for most functions of this website. For the best support, please use the latest version of Chrome, Firefox, or Safari.");
}
else {
var dbVer = 39; //IDB Version (int only)
var recipeObject; //instantiate global variable for module object import
var recipeArray = []; //instantiate global array for module import
var recipeDBver; //instantiate global variable for actual database version (TODO: implement version checking)
var upgradeNeeded = false;
var clearNeeded = false;
var openRequest = indexedDB.open('recipeDatabase', dbVer);
console.log("IDB.js running");
openRequest.onsuccess = function(e) {
console.log('Running onSuccess');
};
openRequest.onerror = function(e) {
console.log('Open Request ERROR');
console.dir(e);
};
openRequest.onupgradeneeded = async function(e) {
var db = e.target.result;
console.log('Running onUpgradeNeeded');
db.onerror = function(errorEvent) {
console.log("onUpgradeNeeded ERROR");
return;
};
importObject = await import("/resources/recipeDB.js");
//TODO: remove debugging
console.log('Module loaded');
console.log(importObject);
recipeObject = importObject.default;
console.log(recipeObject);
recipeDBver = recipeObject.recipeDBver;
console.log(recipeDBver);
recipeArray = recipeObject.recipeArray;
console.log(recipeArray);
upgradeNeeded = true;
if (!db.objectStoreNames.contains('drinks')) {
var storeOS = db.createObjectStore('drinks', {keyPath: 'id'});
storeOS.createIndex('name', 'name', { unique: false });
storeOS.createIndex('type', 'type', { unique: false });
storeOS.createIndex('subtype', 'subtype', { unique: false });
storeOS.createIndex('tags', 'tags', { unique: false });
}
else {
clearNeeded = true;
}
console.log('IDB Upgrade Needed: ' + upgradeNeeded);
console.log('IDB Clear Needed: ' + clearNeeded);
db = e.target.result;
if (clearNeeded == true) {
clearData();
}
else if (upgradeNeeded == true) {
for (var i = 0; i < recipeArray.length; i++) {
addItem(recipeArray[i]);
}
}
};
function clearData() {
var db = openRequest.result;
var transaction = db.transaction(["drinks"], "readwrite");
var objectStore = transaction.objectStore("drinks");
var objectStoreRequest = objectStore.clear();
objectStoreRequest.onerror = function(e) {
console.log('Error clearing data. ', e.target.error.name);
console.dir(e);
};
objectStoreRequest.onsuccess = function(e) {
console.log('Data cleared successfully.')
for (var i = 0; i < recipeArray.length; i++) {
addItem(recipeArray[i]);
}
};
}
function addItem(curItem) {
var db = openRequest.result;
var transaction = db.transaction(['drinks'], 'readwrite');
var store = transaction.objectStore('drinks');
var item = curItem;
var request = store.add(item);
request.onerror = function(e) {
console.log('Error', e.target.error.name);
console.dir(e);
};
request.onsuccess = function(e) {
console.log('Item added: ' + curItem.name);
};
}
}
控制台returns如下:
我假设 onUpgradeNeeded
事件在 JSON 文件能够加载之前超时。
有没有办法延迟超时?如果没有,有没有人知道更好的方法来完成我想做的事情?
onupgradeneeded 事件处理程序需要同步完成。更准确地说,请求 upon/within 版本更改事务 运行 需要在与版本更改事务启动时相同的事件循环滴答中启动。
从你的问题中不清楚,但看起来你正在进行异步调用以加载 json 并等待它加载,而这种等待就是让 versionchange 事务完成的原因,并导致之后发出的所有请求不会在事件循环的同一刻度中发生。
问题从你在控制台看到的就可以看出来。首先我们得到 IDB.js is running
然后你进入你的 onupgradeneeded
处理程序,但是随后,我们没有从那个函数控制台记录任何东西,而是立即看到 onsuccess
处理程序 运行。这是因为您将 onupgradeneeded
处理程序定义为 async
,这意味着该函数在等待导入解析时实质上在 await import("/resources/recipeDB.js");
行停止执行。这实质上意味着就 IDB 事件而言,onupgradeneeded
已完成,需要进入 onsuccess
。正如 Josh 上面所说,这是因为 onupgradeneeded
需要同步解析 。
要解决这个问题,您可以做的是:
- 使
onupgradeneeded
同步 - 更新您的 IDB 架构(创建新的对象存储和索引)
- 导入数据并在成功导入后将它们插入您的数据库
这是使用 IndexedDB 的困难之一:它不是 promised-based,因此使用 promised-based async
函数并不总是能很好地使用它。我通常发现这些 short-comings 需要更多代码来处理它们(例如使用 .then()
调用以便我可以在进行必要的异步活动的同时拥有同步事件处理程序)。
第一步:使用javascript封装json数据。
第二步:在升级事件中使用importScript()
。
第 3 步:运行 你的 indexedDB 脚本 Worker
.