[英]How can I know that IDBCursor reached it's last value?
我試圖使IDBCursor IDBCursor
,像這樣:
/**
* @description Allows asynchronous looping over IDBCursor. The cursor request must be provided and must be new and unused!
*/
class IndexedDBAsyncCursor {
/**
*
* @param {IndexedDBAsyncTable} parentTable
* @param {IDBRequest} cursorRequest
*/
constructor(parentTable, cursorRequest) {
this.cursorRequest = cursorRequest;
this.table = parentTable;
/** @type {Promise<IDBCursor>} **/
this.nextValuePromise = null;
/** @type {IDBCursor} **/
this.lastCursor = null;
this.hasNext = true;
this.hookCursorRequest();
}
/**
* @description Starts waiting for the next value
* @private
*/
makeNextValuePromise() {
if (this.nextValuePromise == null) {
this.rejectPromise = null;
this.resolvePromise =null;
this.nextValuePromise = new Promise((resolve, reject) => {
this.rejectPromise = reject;
this.resolvePromise = resolve;
});
}
}
/**
* Adds event listeners on the cursor
* @private
*/
hookCursorRequest() {
this.makeNextValuePromise();
this.cursorRequest.onsuccess = (event) => {
/** @type {IDBCursor} **/
const cursor = event.target.result;
this.lastCursor = cursor;
if (cursor) {
console.log("[IDB CURSOR] Next value: ", cursor);
this.resolvePromise(cursor);
}
else {
this.hasNext = false;
this.resolvePromise(null);
console.log("[IDB CURSOR] End.");
}
};
this.cursorRequest.onerror = (event) => {
this.hasNext = false;
this.rejectPromise(event);
}
}
/**
* @description Resolves with null or an IDBCursor
* @returns {Promise<IDBCursor>}
*/
async next() {
if (!this.hasNext)
return null;
if (this.lastCursor != null) {
this.makeNextValuePromise();
this.lastCursor.continue();
}
const result = await this.nextValuePromise;
this.nextValuePromise = null;
return result;
}
}
預期用途:
const cursor = new IndexedDBAsyncCursor(this, objectStore.openCursor());
/** @type {IDBCursor} **/
var value = null;
while (value = await cursor.next()) {
if (predicate(value)) {
values.push(value.value);
console.log("[IDB] Found value: ",value.value)
if (oneOnly)
break;
}
else {
console.log("[IDB] Value does not match predicate: ",value.value)
}
}
問題是此代碼:
else {
this.hasNext = false;
this.resolvePromise(null);
console.log("[IDB CURSOR] End.");
}
問題是一旦達到最后一個值,就不會再次調用onsuccess
。 根本不再調用它,而我假設它將最后一次使用null
而不是IDBCursor
來調用它。 但是沒有發生這樣的事情。
如何正確執行此操作?
所提供的代碼對我適用於Chrome,但不適用於Firefox。 僅供參考,這是我以前開車的方式:
indexedDB.deleteDatabase('so');
const open = indexedDB.open('so');
open.onupgradeneeded = e => {
const db = open.result;
const s = db.createObjectStore('s');
for (let i = 0; i < 4; ++i) {
s.put({name: 's' + i, num: i}, i);
}
};
open.onsuccess = async e => {
const db = open.result;
const tx = db.transaction('s');
const objectStore = tx.objectStore('s')
const values = [], oneOnly = false;
const predicate = x => true;
const cursor = new IndexedDBAsyncCursor(this,objectStore.openCursor());
/** @type {IDBCursor} **/
var value = null;
while (value = await cursor.next()) {
if (predicate(value)) {
values.push(value.value);
console.log("[IDB] Found value: ",value.value)
if (oneOnly)
break;
}
else {
console.log("[IDB] Value does not match predicate: ",value.value)
}
}
};
在Chrome瀏覽器中,此記錄:
[IDB CURSOR] Next value: IDBCursorWithValue {value: {…}, source: IDBObjectStore, direction: "next", key: 0, primaryKey: 0}
[IDB] Found value: {name: "s0", num: 0}
[IDB CURSOR] Next value: IDBCursorWithValue {value: {…}, source: IDBObjectStore, direction: "next", key: 1, primaryKey: 1}
[IDB] Found value: {name: "s1", num: 1}
[IDB CURSOR] Next value: IDBCursorWithValue {value: {…}, source: IDBObjectStore, direction: "next", key: 2, primaryKey: 2}
[IDB] Found value: {name: "s2", num: 2}
[IDB CURSOR] Next value: IDBCursorWithValue {value: {…}, source: IDBObjectStore, direction: "next", key: 3, primaryKey: 3}
[IDB] Found value: {name: "s3", num: 3}
[IDB CURSOR] End.
問題是在執行微任務(即Promises的“ then”回調)時,Chrome和Firefox不一致。 Chrome符合規格,微任務在整個任務中執行。 Firefox尚未修復,微任務稍后會執行。 有關更多詳細信息, 請訪問https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/
正如其他喬什(Josh continue()
所說,這是一個問題,因為在Firefox中, continue()
調用最終發生在“ success
”事件處理程序之外,並且不允許這樣做。 您可以通過將以下代碼更改為async next()
實現來查看:
try {
this.lastCursor.continue();
} catch (ex) { console.error(ex.name, ex.message); }
在Firefox中,此日志記錄:“ TransactionInactiveError已針對當前未處於活動狀態或已完成的事務發出了請求。” -由於Firefox執行的微任務包含事件任務之外的下一個調用,因此該事務不再處於活動狀態。
為了在Firefox中解決微任務問題之前完成此工作,無論是否使用下一個值,都必須重組代碼以直接在“ success
”處理程序中調用continue()
。重做跟蹤/生成承諾的方式。
請注意,即使在Firefox中解決了微任務問題后,編寫的代碼仍然很脆弱,因為在while
循環內執行其他異步操作(即引入另一個await
)可能會將continue()
調用推出任務。 因此,如果您想在迭代時執行fetch()
,則會損壞。 如上所述,直接在“ success
”處理程序中執行continue()
將使其更強大。
對這個答案並不完全有信心,但是我認為您不能按照每個游標的請求做出承諾。 您可以在整個游標遍歷中做出承諾,但不能每次迭代都做出承諾。
原因有點復雜,但它與微任務有關,以及在事務未檢測到來自調用cursor.continue
的下一個請求時,事務如何超時。
這是報價:
問題索引數據庫事務與Promises的組合不佳。
事務被定義為具有活動標志,該標志在創建事務時以及從與該事務關聯的源運行IDB事件回調時設置。 當任務完成時,即控件從腳本返回時,活動標志被清除; 例如,在回調的末尾。 僅當標志為true時,才允許事務內的操作(put,get等)。 這意味着您無法在Promise回調中執行操作,因為根據定義,它不是IDB事件回調。 此外,當清除該標志並且沒有掛起的請求時,事務將自動嘗試提交。 這意味着即使解除了先前的限制,事務也將在觸發任何Promise回調之前提交。 如果要完全刪除活動標志機制,則需要引入新的提交模型。
資料來源: https : //github.com/inexorabletash/indexeddb-promises
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.