Вставка больших количеств в блоки объектов объектов 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
})