如何在JavaScript中正确处理IndexedDB API中的错误?

huangapple go评论77阅读模式
英文:

How to properly handle errors in indexeddb api in javascript?

问题

以下是代码的中文翻译部分:

 JavaScript 我有一个类来处理请求我想确保错误处理已经设置好了我想在每个请求上打开数据库连接并关闭它到目前为止我有以下代码

export default class IndexedDBStorage {
    
    #name: string;
    
    constructor(name: string) {
        this.#name = name;
    }
    
    private async GetDB(): Promise<IDBDatabase> {
        return new Promise((resolve, reject) => {
            
            // https://javascript.info/indexeddb
            const request = window.indexedDB.open(this.#name, 1);
            
            request.onupgradeneeded = () => {
                //this.#db = request.result;
                if (!request.result.objectStoreNames.contains(this.#name)) {
                    request.result.createObjectStore(this.#name, { keyPath: 'id', autoIncrement: true });
                }
            };
            
            request.onerror = () => {
                reject("为什么不允许我的 Web 应用使用 IndexedDB?! " + request.error?.code);
            };
            
            request.onsuccess = () => {
                //request.result.onerror = () => {
                // 用于处理针对该数据库请求的所有错误的通用错误处理程序
                // reject("数据库错误:" + request.error?.code);
                // };
                
                resolve(request.result);
            };
        });
    }
    
    async GetAllItems<T>(): Promise<Array<T>> {
        return new Promise((resolve, reject) => {
            this.GetDB().then(db => {
                const transaction = db.transaction(this.#name);
                const store = transaction.objectStore(this.#name);
                const datarequest = store.getAll();
                db.close();
                datarequest.onsuccess = function () {
                    resolve(datarequest.result);
                };
                datarequest.onerror = function () {
                    reject("错误 " + datarequest.error);
                };
            }).catch(err => {
                console.error(err);
            });
        });
    }
}

关于你的问题,两部分的区别在于处理不同层次的错误:

  1. request.onerror 处理数据库请求的错误,例如数据库连接失败等。
  2. datarequest.onerror 处理数据请求的错误,例如在获取数据时发生的问题。

GetDB 函数中,request.onerror 处理数据库级别的错误,而在 GetAllItems 函数中,datarequest.onerror 处理具体数据请求的错误。这两部分的处理是针对不同层次的错误情况。

英文:

In javascript, I have a class here to handle requests. I want to make sure I have all the error handling set up ok. I want to open the connection to the db and close it on each request. I have this code so far

export default class IndexedDBStorage {
#name:string;
constructor(name:string) {
this.#name = name;
}
private async GetDB():Promise&lt;IDBDatabase&gt; {
return new Promise((resolve, reject) =&gt; {
// https://javascript.info/indexeddb
const request = window.indexedDB.open(this.#name, 1);
request.onupgradeneeded = () =&gt; {
//this.#db = request.result;
if (!request.result.objectStoreNames.contains(this.#name)) {
request.result.createObjectStore(this.#name, {keyPath: &#39;id&#39;, autoIncrement:true});
}
};
request.onerror = () =&gt; {
reject(&quot;Why didn&#39;t you allow my web app to use IndexedDB?! &quot; + request.error?.code);
};
request.onsuccess = () =&gt; {
//request.result.onerror = () =&gt; {
// Generic error handler for all errors targeted at this database&#39;s requests!
//    reject(&quot;Database error: &quot; + request.error?.code);
//};
resolve(request.result);
};
});
}
async GetAllItems&lt;T&gt;():Promise&lt;Array&lt;T&gt;&gt; {
return new Promise((resolve, reject) =&gt; {
this.GetDB().then(db =&gt; {
const transaction = db.transaction(this.#name);
const store = transaction.objectStore(this.#name);
const datarequest = store.getAll();
db.close();
datarequest.onsuccess = function() {
resolve(datarequest.result);
};
datarequest.onerror = function() {
reject(&quot;Error &quot; + datarequest.error);
}; 
}).catch(err =&gt; {
console.error(err);
});
});
}
}

Also wanted to know, what is the difference between this part inside the GetDB function. This is the onerror event of the db itself, which is setup in the onsuccess event.

            //request.result.onerror = () =&gt; {
// Generic error handler for all errors targeted at this database&#39;s requests!
//    reject(&quot;Database error: &quot; + request.error?.code);
//};

And this part

            datarequest.onerror = function() {
reject(&quot;Error &quot; + datarequest.error);
}; 

?

Thanks

EDIT:

export default class IndexedDBStorage {
#name:string;
constructor(name:string) {
this.#name = name;
}
private async GetDB():Promise&lt;IDBDatabase&gt; {
return new Promise((resolve, reject) =&gt; {
const request = window.indexedDB.open(this.#name, 1);
request.onerror = (event: Event) =&gt; {
// this function catches all errors
// all errors in database, transaction or request levels should be caught and handled there
const errorEvent = event as Event &amp; {error:string};
console.error(errorEvent.error);
alert(errorEvent.error);
reject(errorEvent.error);
};
request.onblocked = function () {
// this event shouldn&#39;t trigger if we handle onversionchange correctly
// it means that there&#39;s another open connection to the same database
// and it wasn&#39;t closed after db.onversionchange triggered for it
const message = &#39;Database is currently blocked, page needs to reload, click ok to reload.&#39;;
alert(message);
window.location.reload();
//console.log(message);
//reject(message);
};
request.onupgradeneeded = (event: IDBVersionChangeEvent) =&gt; {
const db = request.result;
console.log(&#39;idb onupgradeneeded firing&#39;);
switch(event.oldVersion) { // existing db version
case 0:
// version 0 means that the client had no database
// perform initialization
console.log(&#39;Database currently at version 0 which means that the client had no database&#39;);
console.log(`Upgrading to version ${db.version}`);
request.result.createObjectStore(this.#name, {keyPath: &#39;id&#39;, autoIncrement:true});
case 1:
// client had version 1
// update
console.log(&#39;Database currently at version 1 which means that the database already exists&#39;);
}
};
request.onsuccess = () =&gt; {
const db = request.result;
db.onversionchange = function() {
db.close();
const message = &quot;Database is outdated, page needs to reload, click ok to reload.&quot;;
alert(message);
window.location.reload();
};
resolve(db);
};
});
}
async GetAllItems&lt;T&gt;():Promise&lt;Array&lt;T&gt;&gt; {
return new Promise((resolve, reject) =&gt; {
this.GetDB().then(db =&gt; {
const transaction = db.transaction(this.#name);
const store = transaction.objectStore(this.#name);
const datarequest = store.getAll();
db.close();
datarequest.onsuccess = function() {
resolve(datarequest.result);
};
}).catch(err =&gt; {
reject(err);
});
});
}
}

答案1

得分: 3

  • 请求可能出错,当请求出错时,它会获取一个 error 属性,带有一个值。
  • 事务可能会出错,但错误只能从事务中出错的请求之一中访问。
  • 打开数据库的请求和获取/放置/添加等请求之间没有太大的区别。从技术上讲,一个是类型 IDBOpenRequest,另一个是类型 IDBRequest,我认为 IDBOpenRequest 扩展了 IDBRequest。
  • 如果要处理事务错误,通过事件访问错误,因为 event.target 将指向出错的请求,所以 event.target.error,也就是 request.error,是错误对象,而 event 只是事件本身,而不是错误本身。
  • 可以将 IDBOpenRequest 包装在一个 Promise 中,也可以将请求或事务包装在一个 Promise 中。
  • 通常情况下,除了打开请求外,最好避免将请求包装在 Promises 中,总是将外部事务包装,因为 Promise 微任务可能出现问题,以及 IndexedDB 事务在事件循环中超时的可能问题。
  • 由于您在 GetAllItems 中捕获了 Promise 拒绝和用于 catch 的回调是一个 void 函数,您实际上是在抑制错误并返回一个始终解析为数组或 undefined 的 Promise。
  • 您没有考虑到在打开请求中可能发生的被阻塞事件,这里本质上是 Promise 无法解决的问题(直到数据库解锁,因为并发版本更改事务完成),因此这段代码不一定总是有效。
  • 我建议不要监听冒泡到数据库的错误,总是在请求级别监听错误(除非将事务包装在 Promise 中,在这种情况下,请在事务级别监听错误,但通过 event.target.error 访问错误,该属性指向事务中出错的请求),我强热建议完全忽略 IndexedDB 的冒泡功能。
英文:
  • requests can error, when a request errors it acquires an error property with a value
  • transactions can error, but the error will only be accessible from the one of the erring requests in the transaction
  • there is no big difference between the request to open the database and a request to get/put/add/etc. technically one is type IDBOpenRequest and one is type IDBRequest, i think IDBOpenRequest extends IDBRequest
  • if handling the transaction error, access the error via the event, because event.target will point to the request that erred, so event.target.error, aka request.error, is the error object, where as event is just the event and not the error itself
  • you can wrap the IDBOpenRequest in a promise, and you can wrap a request or transaction in a promise
  • generally avoid wrapping requests in promises except for the open request, always wrap the outer transaction, because of possible issues with promise microtasks and how indexeddb transactions time out in the event loop
  • because you catch the promise rejection in GetAllItems and the callback you are using for the catch is a void function, you are effectively suppressing the error and returning a promise that always resolves to either an array or undefined
  • you are not considering the blocked event that can happen in the open request, where essentially the promise sits unsettled indefinitely (until the database is unblocked because the concurrent version change transactions complete), so this code is not always going to work so well
  • i suggest not listening for errors that bubble up to the database, always listen for errors at the request level (except for when wrapping a transaction in a promise, in that case listen at transaction level but access the error via event.target.error, which points to the request in the transaction that erred), i highly suggest just ignoring the bubbling feature of indexeddb entirely

huangapple
  • 本文由 发表于 2023年4月4日 05:57:02
  • 转载请务必保留本文链接:https://go.coder-hub.com/75924047.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定