簡體   English   中英

我怎么知道IDBCursor達到了最后一個值?

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM