您如何使用异步调用解决 IndexedDb 事务
How do you work around IndexedDb Transaction with async call
我有一个从打开页面开始的工作器。工作人员打开一个事务,检查记录中是否存在值,如果不存在,则需要进行提取调用以获取该值。
我遇到的问题是我在 read/write 事务中执行此操作,但提取是异步的,它会在返回提取之前提交事务。如果我可以保证只有一个页面同时访问逻辑,那么我会在没有事务的情况下这样做,例如获取数据,检查它,如果需要获取值和更新。但是我不能保证单页。
所以问题是如何保证第一页开始获取新值的过程后第二页不会开始获取新值的过程?
async function start(message) {
var result = await new Promise(async (resolve, reject) => {
let transaction = db.transaction(["Client"], "readwrite")
let objectStore = transaction.objectStore("Client");
let request = objectStore.get(message.client_name);
request.onerror = (event) => {
reject(event);
};
request.onsuccess = async () => {
try {
let data = request.result
if (!data) {
data = {
client_name: message.client_name,
sequence: 0,
api_base_url: message.api_base_url || 'https://api.local'
}
}
if (!data.last_access || dateDiff(Date.parse(data.last_access), new Date(), "m") > 20) {
let response = await fetch(data.api_base_url + '/v1/hello');
let responseData = await response.json();
data.id = responseData.id;
data.last_access = responseData.dt;
}
objectStore.put(data); // <-- Here is where it errors
resolve(data.sequence);
} catch (error) {
reject(error);
}
}
});
return result;
}
目前还没有好的方法。
如果您使用的是 Dexie,它有 a waitFor
function 可以让您在等待另一个承诺解决时保持交易打开。但是...
Use with caution as it may put unnecessary CPU load on the browser. A separate task will keep the transaction alive by propagating dummy-requests on the transaction while the given promise is being executed.
如果您不使用 Dexie,您也可以自己实现同样的功能。继续发出请求,同时检查您的提取承诺是否已解决。
最好找到一些方法在获取期间不必保持事务处于活动状态。就像在 localStorage 中存储一个标志以防止第二次调用做任何事情一样?或者如果第二次提取在第一次完成之前开始,则取消第一次提取?我不确定你到底想做什么,但通常有一些方法可以解决 IndexedDB 的这个限制。
一种方法是使用 SharedWorker 来执行该操作。
这样,您的所有页面实例(及其 Dedicated Worker)都将使用同一个 SharedWorker,它能够跟踪事务状态,从而能够确定是否应该发出新请求.
尽管 Safari 不支持它,但我想总比没有好。
我有一个从打开页面开始的工作器。工作人员打开一个事务,检查记录中是否存在值,如果不存在,则需要进行提取调用以获取该值。
我遇到的问题是我在 read/write 事务中执行此操作,但提取是异步的,它会在返回提取之前提交事务。如果我可以保证只有一个页面同时访问逻辑,那么我会在没有事务的情况下这样做,例如获取数据,检查它,如果需要获取值和更新。但是我不能保证单页。
所以问题是如何保证第一页开始获取新值的过程后第二页不会开始获取新值的过程?
async function start(message) {
var result = await new Promise(async (resolve, reject) => {
let transaction = db.transaction(["Client"], "readwrite")
let objectStore = transaction.objectStore("Client");
let request = objectStore.get(message.client_name);
request.onerror = (event) => {
reject(event);
};
request.onsuccess = async () => {
try {
let data = request.result
if (!data) {
data = {
client_name: message.client_name,
sequence: 0,
api_base_url: message.api_base_url || 'https://api.local'
}
}
if (!data.last_access || dateDiff(Date.parse(data.last_access), new Date(), "m") > 20) {
let response = await fetch(data.api_base_url + '/v1/hello');
let responseData = await response.json();
data.id = responseData.id;
data.last_access = responseData.dt;
}
objectStore.put(data); // <-- Here is where it errors
resolve(data.sequence);
} catch (error) {
reject(error);
}
}
});
return result;
}
目前还没有好的方法。
如果您使用的是 Dexie,它有 a waitFor
function 可以让您在等待另一个承诺解决时保持交易打开。但是...
Use with caution as it may put unnecessary CPU load on the browser. A separate task will keep the transaction alive by propagating dummy-requests on the transaction while the given promise is being executed.
如果您不使用 Dexie,您也可以自己实现同样的功能。继续发出请求,同时检查您的提取承诺是否已解决。
最好找到一些方法在获取期间不必保持事务处于活动状态。就像在 localStorage 中存储一个标志以防止第二次调用做任何事情一样?或者如果第二次提取在第一次完成之前开始,则取消第一次提取?我不确定你到底想做什么,但通常有一些方法可以解决 IndexedDB 的这个限制。
一种方法是使用 SharedWorker 来执行该操作。
这样,您的所有页面实例(及其 Dedicated Worker)都将使用同一个 SharedWorker,它能够跟踪事务状态,从而能够确定是否应该发出新请求.
尽管 Safari 不支持它,但我想总比没有好。