Вставка больших количеств в блоки объектов объектов IndexedDB UI

Я хочу сохранить ~ 35000 объектов в моем объекте storeedDB. Я использую ниже код для вставки.

AddListings = function (x2j_list_new, callback) {   
    var transaction = db.transaction(["listings"], IDBTransaction.READ_WRITE);
    var count = 0;
    transaction.oncomplete = function (event) {
        if (callback) {
            console.log('x2jShowListing Added ' + count + '/' + x2j_list_new.length);
                callback([count, x2j_list_new.length]);
            }
    };
    transaction.onerror = function (e) {
       console.log("myError: ", e);  
       if (callback) {
          callback(false);
       }
    };
    var store = transaction.objectStore("listings");

    $.each(x2j_list_new, function (index0, item0) {
        var request = store.put(item0);
        request.onsuccess = function (event) {
            count++;
            // event.target.result  
            };
        });
    });        
};

Вышеприведенный код работает нормально, но цикл и вставка более ~ 35000 объектов делает пользовательский интерфейс невосприимчивым в течение ~ 200 секунд. Я думал, может быть, я могу использовать WebWorkers, но IndexedDB недоступен внутри WebWorkers. Я попытался найти способ для массовой вставки, не смог найти его. Любые идеи о том, как вставлять большие количества объектов без блокировки пользовательского интерфейса?

Ответы

Ответ 1

Вы на правильном пути, но вы просите браузер хранить 35 000 объектов, прежде чем у него будет возможность закончить его хранение. Здесь код, который асинхронно ожидает завершения одного запроса перед запуском следующего (но с использованием той же транзакции):

    openRequest = window.indexedDB.open("MyDatabase", 1);
    openRequest.onerror = function(event) {
        console.error(event);
    };
    openRequest.onsuccess = function (event) {
        var db = openRequest.result;
        db.onerror = function(event) {
            // Generic error handler for all errors targeted at this database requests
            console.error(event.target);
            window.alert("Database error: " + event.target.wePutrrorMessage || event.target.error.name || event.target.error || event.target.errorCode);
        };
        var transaction = db.transaction('item', "readwrite");
        var itemStore = transaction.objectStore("item");
        putNext();

        function putNext() {
            if (i<items.length) {
                itemStore.put(items[i]).onsuccess = putNext;
                ++i;
            } else {   // complete
                console.log('populate complete');
                callback();
            }
        }           
    };      

Ответ 2

Вы делаете все правильно, используя обратные вызовы.

API Webworker еще не реализован ни одним крупным браузером. Интересно, что ожидается синхронность. Обычный API является асинхронным по точной причине, которую вы описываете - он не должен блокировать поток пользовательского интерфейса.

Использование обратных вызовов - это способ избежать блокировок, но при 35 тыс. Объектах вы ясно видите, как эта парадигма разрушается. К сожалению, производительность IDB пока не соответствует стандартам WebSQL из тех тестов, которые я видел.

В Chrome LevelDB появились некоторые экспериментальные бэкэнды (FF - SQLite), но я думаю, что ваш опыт доказывает, что есть место для улучшений.

Ответ 3

Дикая догадка с моей стороны, но если WebSQL доступен из так называемой "фоновой страницы" и предполагая, что пропускная способность обмена сообщениями между фронтом и обратной связью не блокирует пользовательский интерфейс таким же образом, возможно, может использоваться справочная страница с внутристраничным сообщением?

Ответ 4

Я разделяю массив на куски 500 и используя setTimeout вместо цикла. Теперь пользовательский интерфейс немного лучше, чем раньше.

Ответ 5

IndexedDB теперь поддерживается в WebWorker. Используя современный JS, вы можете написать что-то вроде:

const transaction = db.transaction('item', 'readwrite')
const itemStore = transaction.objectStore('item')
const promises = []

for (let item of x2j_list_new) {
  promises.push(new Promise((resolve, reject) => {
    const request = store.add(item)

    request.onsuccess = resolve
    request.onerror = reject
  }))
}

Promise.all(promises).then(() => {
  callback()
}).catch(e => {
  // handle error
})