Javascript 我怎么知道IDBCursor到达了它';最后的价值是多少?

Javascript 我怎么知道IDBCursor到达了它';最后的价值是多少?,javascript,asynchronous,promise,indexeddb,Javascript,Asynchronous,Promise,Indexeddb,我试着像这样承诺: /** * @description Allows asynchronous looping over IDBCursor. The cursor request must be provided and must be new and unused! */ class IndexedDBAsyncCursor { /** * * @param {IndexedDBAsyncTable} parentTable * @param {

我试着像这样承诺:

/**
 * @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;
    }
}
问题在于此代码:

    else {
        this.hasNext = false;
        this.resolvePromise(null);
        console.log("[IDB CURSOR] End.");
    }
问题是,一旦达到最后一个值,
onsuccess
就不会再次调用。它不再被调用,而我假设它将被最后一次调用,使用
null
而不是
IDBCursor
。但没有这样的事情发生


如何正确地做到这一点?

对此答案并不完全有信心,但我认为您无法按游标请求完成承诺。您可以在整个光标移动过程中做出承诺,但不是每次迭代

原因有点复杂,但它与微任务有关,也与事务在没有检测到调用
游标的下一个请求时如何超时有关。请及时继续

以下是报价:

有问题的索引数据库事务与承诺的组合很差

事务被定义为具有活动标志,该标志在 事务被创建,并且当来自源的IDB事件回调 运行与该事务关联的。活动标志被清除 当任务完成时,即当控制从脚本返回时;对于 例如,在回调的末尾。事务中的操作 (put、get等)仅在标志为真时才允许。这意味着 您不能在承诺回调中执行操作,因为 根据定义,不是IDB事件回调。此外,交易 当清除标志且存在以下情况时自动尝试提交 没有未决的请求。这意味着即使之前的 取消限制后,事务将在任何 我被解雇了。如果要启用活动标志机制 如果完全删除,则需要引入新的提交模型


来源:

对这个答案并不完全有信心,但我不认为你能做到每一个游标请求的承诺。您可以在整个光标移动过程中做出承诺,但不是每次迭代

原因有点复杂,但它与微任务有关,也与事务在没有检测到调用
游标的下一个请求时如何超时有关。请及时继续

以下是报价:

有问题的索引数据库事务与承诺的组合很差

事务被定义为具有活动标志,该标志在 事务被创建,并且当来自源的IDB事件回调 运行与该事务关联的。活动标志被清除 当任务完成时,即当控制从脚本返回时;对于 例如,在回调的末尾。事务中的操作 (put、get等)仅在标志为真时才允许。这意味着 您不能在承诺回调中执行操作,因为 根据定义,不是IDB事件回调。此外,交易 当清除标志且存在以下情况时自动尝试提交 没有未决的请求。这意味着即使之前的 取消限制后,事务将在任何 我被解雇了。如果要启用活动标志机制 如果完全删除,则需要引入新的提交模型


来源:

所展示的代码在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和Firefox在执行微任务(即承诺的“then”回调)时不一致。Chrome符合规范,微任务在整个任务中执行。Firefox尚未修复,微任务稍后执行。详情请浏览

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

      try {
        this.lastCursor.continue();
      } catch (ex) { console.error(ex.name, ex.message); }
在Firefox中,此日志记录:“TransactionActiveError针对当前未激活或已完成的事务发出了请求。”——因为Firefox执行包含事件任务外的下一个调用的微任务,所以该事务不再处于活动状态

为了在Firefox修复微任务问题之前实现这一点,需要重新构造代码,直接在“
success
”处理程序中调用
continue()
,而不管是否使用下一个值,这将需要重新设计跟踪/生成承诺的方式


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

所示的代码在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和Firefox在执行微任务(即承诺的“then”回调)时不一致。Chrome符合规范,微任务在整个任务中执行。Firefox尚未修复,微任务稍后执行。详情请浏览

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

      try {
        this.lastCursor.continue();
      } catch (ex) { console.error(ex.name, ex.message); }
在Firefox中记录:“TransactionActiveError针对当前未激活或已完成的事务发出请求。”——因为Firefox执行包含以下内容的微任务: