Safari TransactionActiveError:无法执行';获取';在';IDBObjectStore';:事务处于非活动状态或已完成

Safari TransactionActiveError:无法执行';获取';在';IDBObjectStore';:事务处于非活动状态或已完成,safari,transactions,promise,indexeddb,Safari,Transactions,Promise,Indexeddb,这似乎是一个只用于狩猎的bug。据我所知,它不会出现在Chrome中。我有一个非常标准的IndexedDB设置。我调用initDb,保存结果,这为我调用DB提供了一种很好的方法 var initDb=function(){ //Setup DB.whenDB是我们在执行任何数据库请求之前使用的承诺,因此我们知道数据库已经完全设置好了。 parentDb=null; var whenDb=新承诺(功能(解决、拒绝){ var DBOpenRequest=window.indexedDB.open(

这似乎是一个只用于狩猎的bug。据我所知,它不会出现在Chrome中。我有一个非常标准的IndexedDB设置。我调用initDb,保存结果,这为我调用DB提供了一种很好的方法

var initDb=function(){
//Setup DB.whenDB是我们在执行任何数据库请求之前使用的承诺,因此我们知道数据库已经完全设置好了。
parentDb=null;
var whenDb=新承诺(功能(解决、拒绝){
var DBOpenRequest=window.indexedDB.open('groceries');
DBOpenRequest.onsuccess=函数(事件){
parentDb=DBOpenRequest.result;
解决();
};
DBOpenRequest.onupgradeneeded=函数(事件){
var localDb=event.target.result;
localDb.createObjectStore('unique'{
密钥路径:“id”
});
};
});
//makeRequest需要返回IndexedDB请求对象。
//这个函数只是将其封装在一个承诺中。
var request=函数(makeRequest,键){
返回新承诺(功能(解决、拒绝){
var-request=makeRequest();
request.onerror=函数(){
拒绝(“请求错误”);
};
request.onsuccess=函数(){
if(request.result==未定义){
拒绝(键+‘未找到’);
}否则{
解决(请求、结果);
}
};
});
};
//打开一个非常典型的事务
var transact=函数(类型、店名){
//确保数据库已设置,然后打开事务
返回whenDb.then(函数(){
var transaction=parentDb.transaction([storeName],类型);
transaction.oncomplete=函数(事件){
console.log('transcomplete')
};
transaction.onerror=函数(事件){
console.log('由于错误而未打开事务:'+Transaction.error);
};
return transaction.objectStore(storeName);
});
};
//打开事务并返回等待DB查询完成的标准Javascript承诺的快捷方式函数
var read=函数(storeName,key){
返回transact('readonly',storeName)。然后返回(函数(transactionStore){
返回请求(函数(){
返回transactionStore.get(键);
},键);
});
};
//一种测试函数,将以前的事务、请求和读取函数合并为一个。
var测试=函数(){
返回whenDb.then(函数(){
var transaction=parentDb.transaction(['unique'],'readonly');
transaction.oncomplete=函数(事件){
console.log('transcomplete')
};
transaction.onerror=函数(事件){
console.log('由于错误而未打开事务:'+Transaction.error);
};
var store=transaction.objectStore('unique');
返回新承诺(功能(解决、拒绝){
var request=store.get('groceryList');
request.onerror=函数(){
console.log(request.error);
拒绝(“请求错误”);
};
request.onsuccess=函数(){
if(request.result==未定义){
拒绝(键+‘未找到’);
}否则{
解决(请求、结果);
}
};
});
});
};
//返回用于db交互的对象
返回{
读:读,
测试:测试
};
};
var db=initDb();
在Safari中调用
db.read('unique','test')
时,我得到错误:

TransactionInactiveError: Failed to execute 'get' on 'IDBObjectStore': The transaction is inactive or finished
Chrome中的相同调用没有错误,只是预期的承诺返回。奇怪的是,在Safari中调用db.test函数也能正常工作。从字面上看,Safari中将工作分成两个函数似乎是导致此错误的原因

在所有情况下,
transcomplete
都是在抛出错误(在Safari bug的情况下)或返回正确的值(应该发生的情况下)后记录的。因此,在抛出表示事务处于非活动状态或已完成的错误之前,事务尚未关闭


很难在这里找到问题。

嗯,我对我的答案没有信心,但我的第一个猜测是,创建事务和启动请求之间发生的暂停会导致事务超时并变得不活动,因为它没有发现任何活动的请求,这样,稍后尝试启动的请求将在非活动事务上启动。这可以通过在javascript事件循环的同一时间段(相同的记号)启动请求而不是延迟请求的启动来轻松解决

错误最有可能出现在以下几行中:

var store = transaction.objectStore('unique');
return new Promise(function(resolve, reject) {
   var request = store.get('groceryList');
您需要立即创建请求以避免此错误:

var store = transaction.objectStore('unique');
var request = store.get('groceryList');
解决这一问题的一种方法可能只是以不同的方式处理代码。承诺是可组合的。使用承诺的代码通常希望将控制权返回给调用方,以便调用方可以控制流。当前编写的某些函数违反了此设计模式。通过简单地使用更合适的设计模式,您可能不会遇到此错误,或者至少您能够更容易地识别问题

另外一点是混合使用全局变量。像
parentDb
db
这样的变量可能会在某些平台上引起问题,除非您真的是异步代码专家

例如,从一个简单的connect或open函数开始,该函数解析为一个开放的IDBB数据库变量
function connect(name) {
  return new Promise(function(resolve, reject) {
    var openRequest = indexedDB.open(name);
    openRequest.onsuccess = function() {
      var db = openRequest.result;
      resolve(db);
    };
  });
}
connect('groceries').then(function(db) {
  // do stuff with db here
});
function getGroceryList(db, listId) {
   return new Promise(function(resolve, reject) {
     var txn = db.transaction('unique');
     var store = txn.objectStore('unique');
     var request = store.get(listId);
     request.onsuccess = function() {
        var list = request.result;
        resolve(list);
     };
     request.onerror = function() {
        reject(request.error);
     };
   });
}
connect().then(function(db) {
  return getGroceryList(db, 'asdf');
}).catch(error);