Search code examples
javascriptindexeddbweb-worker

IndexedDB main thread & WebWorker eventlisteners


Hello I'm building an application in which I use indexeDB. Based on app config I' m able to choose if I should use indexeDB from WebWorker or from the main UI thread. A connection from the main UI thread is always being made regardless of the config. However based on the config the heavy lifting is being done from the worker if selected.

Example of import prototype:

Database.prototype.importItem = function(item, callback) {

    if (this.settings.useWorker) {

        this.postMessage({
            cmd: "importItem",
            data: item
        }, callback);

    } else {

        var that = this;
        var transaction = this.db.transaction(this.settings.collection, "readwrite");
        var objectStore = transaction.objectStore(this.settings.collection);
        var request = objectStore.add(item);

        request.onerror = function(evt) {
            if (callback && callback instanceof Function) {
                callback(new ApplicationError("importItem error", evt.target.error), null);
            }
        };

        request.onsuccess = function(evt) {
            if (callback && callback instanceof Function) {
                callback(null, evt.target.result);
            }
        };

        transaction.oncomplete = function(evt) {};

    }
};

What I'm curious about thought is the event listeners that are required from indexedDB, example:

Database.prototype.connect = function() {

    if (!this.supported()) {
        return this.emit("error", new ApplicationError("IndexedDB not supported!"));
    }

    var that = this;
    var openRequest = window.indexedDB.open(this.settings.dbName, this.settings.dbVersion);

    /**
     * [onerror description]
     * @param  {[type]} evt [description]
     * @return {[type]}     [description]
     */
    openRequest.onerror = function(evt) {
        that.emit("error", new ApplicationError("openRequest.onerror error", evt.target.error));
    };

    /**
     * [onblocked description]
     * @param  {[type]} evt [description]
     * @return {[type]}     [description]
     */
    openRequest.onblocked = function(evt) {
        // If some other tab is loaded with the database, then it needs to be closed
        // before we can proceed.
        alert("Please close all other tabs with this site open!");
    };

    /**
     * [onsuccess description]
     * @param  {[type]} evt [description]
     * @return {[type]}     [description]
     */
    openRequest.onsuccess = function(evt) {

        that.db = evt.target.result;

        that.db.onerror = function(evt) {
            logger.warn("openRequest.onsuccess error", evt.target);
        };

        that.db.onabort = function(evt) {
            logger.warn("openRequest.onsuccess abort", evt.target);
        };

        that.db.onversionchange = function(evt) {
            that.db.close();
            that.emit("versionchange", new ApplicationError("openRequest.onsuccess version change", evt.target));
        };

        if (that.settings.useWorker) {
            that.requestWorker();
        } else {
            that.emit("connect");
        }

    };

    /**
     * [onupgradeneeded description]
     * @param  {[type]} evt [description]
     * @return {[type]}     [description]
     */
    openRequest.onupgradeneeded = function(evt) {

        var stores = {};

        that.db = evt.target.result;

        that.db.onerror = function(evt) {
            logger.warn("openRequest.onupgradeneeded error", evt.target);
        };

        that.db.onabort = function(evt) {
            logger.warn("openRequest.onupgradeneeded abort", evt.target);
        };

        that.db.onversionchange = function(evt) {
            that.db.close();
            that.emit("versionchange", new ApplicationError("openRequest.onupgradeneeded version change", evt.target));
        };

        // Check for the objectStore - collection and delete it if exists
        if (that.db.objectStoreNames.contains(that.settings.collection)) {
            that.db.deleteObjectStore(that.settings.collection);
        }

        // Create new objectStore
        stores[that.settings.collection] = that.db.createObjectStore(that.settings.collection, {
            keyPath: that.settings.indexes[0]
        });

        // Create database indexes
        that.settings.indexes.forEach(function(index) {
            stores[that.settings.collection].createIndex(index, index, {
                unique: false
            });
        });

        that.upgraded = true;
        that.emit("upgrade");

    };

    /**
     * [onbeforeunload description]
     * @param  {[type]} evt [description]
     * @return {[type]}     [description]
     */
    window.onbeforeunload = function(evt) {
        that.db.close();
    };

};

Since I'm connecting always first from the main ui and then from the worker, should I just listen for events like "onblocked, versionchange" only in the main UI thread and not in the worker? I Suppose it wont be needed to listen from both threads?

UPDATE

I know that this is a weird implementation but the reason I was thinking about it is because I'm building an app on a very machine that has 3GB of ram and 2 cores... Also I have a method that iterates all records in a collection from my db. What I was thinking is pass each record retrieved to another method for image loading & manipulation and then perhaps call the callback... That would limit the memory usage since it would be done in a serie if I'm not mistaken. However I'm not sure if the transaction will still be alive.

Bottom line that's the main reason I was thinking for 2 connections (because of 1 method) and I was wondering if I could avoid double event listeners. Maybe have some of them on 1 thread.


Solution

  • You're doing it wrongly. IndexedDB connection should not be opened on both threads. It makes your app architecture unnecessarily complicated. Data retrieved from IndexedDB can be easily exchanged with UI threads via Web workers' messaging channel.