gpt4 book ai didi

javascript - 我如何知道 IDBCursor 达到了它的最后一个值?

转载 作者:行者123 更新时间:2023-12-02 20:33:21 28 4
gpt4 key购买 nike

我尝试 promise 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.

问题在于 Chrome 和 Firefox 在执行微任务(即 Promises 的“then”回调)时不一致。 Chrome 是按规范设计的,微任务是在整体任务中执行的。 Firefox 尚未修复,微任务稍后执行。更多详情请访问https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/

正如 other-Josh 暗示的那样,这是一个问题,因为在 Firefox 中,continue() 调用最终发生在“success”事件处理程序之外,而这不是允许。您可以通过将以下代码更改为 async next() 实现来查看:

      try {
this.lastCursor.continue();
} catch (ex) { console.error(ex.name, ex.message); }

在 Firefox 中,此日志记录:“TransactionInactiveError 针对当前不活动或已完成的事务发出了请求。” -- 由于 Firefox 执行的微任务包含事件任务之外的下一个调用,因此事务不再处于事件状态。

要在 Firefox 中修复微任务问题之前实现此功能,需要重构代码以直接在“success”中调用 continue()处理程序,无论是否使用下一个值,这将需要重新设计跟踪/生成 promise 的方式。

请注意,即使微任务问题在 Firefox 中得到解决,所编写的代码仍然很脆弱,因为在 while 循环内执行其他异步操作(即引入另一个 await ) 可能会将 continue() 调用推出任务。所以如果你想例如在迭代时执行 fetch() 会中断。如上所述,直接在“success”处理程序中执行 continue() 将会使其更加健壮。

关于javascript - 我如何知道 IDBCursor 达到了它的最后一个值?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47934408/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com