0

I have the following snippet of code:

function open_db(dbname, dbversion, upgrade, onblocked) {
    if (upgrade === undefined) {
        upgrade = function basic_init(ev) {
            …
        };
    }
    if (onblocked === undefined) {
        onblocked = function onblocked(ev) {
            throw ev;
        };
    }
    let req = window.indexedDB.open(dbname, dbversion);
    return new Promise((resolve, reject) => {
        req.onsuccess = ev => resolve(ev.target.result);
        req.onerror = ev => reject(ev.target.error);
        req.onupgradeneeded = ev => {
            try {
                return upgrade(ev);
            } catch (error) {
                reject(error);
                ev.target.onsuccess = ev => ev.target.close(); // IS THIS LINE NECESSARY?
                throw error; // IS THIS LINE UNNECESSARY?
            }
        };
        req.onblocked = ev => {
            try {
                return onblocked(ev);
            } catch (error) {
                reject(error);
                ev.target.onsuccess = ev => ev.target.close(); // IS THIS LINE NECESSARY?
                throw error; // IS THIS LINE UNNECESSARY?
            }
        };
    });
}

If the .onblocked or .onupgradeneeded handlers throw a native error, will that cancel the open attempt? Or will the IDBOpenDBRequest object ignore such errors and steam on ahead obliviously until I manually close the db if/after it's opened?

In a nutshell: are the commented lines of code necessary? Are they sufficient to prevent a dangling open handle?

Is there a better way to cancel the request-to-open, rather than just adding .onsuccess = ev => … .close()?

1 Answers1

2

You're asking the right question ("Is there a better way to cancel the request-to-open... ?") and the answer is: no, not as currently defined/implemented. All you can do is make the open a no-op by aborting the upgrade.

Throwing in a blocked handler doesn't have specified special behavior; doing anything here should be unnecessary, as it will be followed by an upgradeneeded eventually.

On upgradeneeded, closing the connection before the upgrade completes will terminate the request and abort the upgrade, so the version won't change. There are a handful of ways to do this:

Note that after seeing upgradeneeded, waiting until success (which your code does) means the transaction will have completed, and the upgrade will have happened.

So in your sample code, the throw statements are effectual (they will abort the upgrade), while the close calls are not. The success event should never fire, in that case, which makes adding handlers for success which close the connection irrelevant.

Joshua Bell
  • 7,727
  • 27
  • 30
  • >_closing the connection before the upgrade completes_< by this, do you mean that `db.close` is called before `onupgradeneeded` `return`s? – JamesTheAwesomeDude Jul 18 '22 at 18:58
  • _n.b._ for anyone else reading this answer: [`IDBRequest.transaction`](https://w3c.github.io/IndexedDB/#dom-idbrequest-transaction) _is_ a getter attribute that returns the _current_ transaction, and does _not_ create a new one like [`IDBDatabase.transaction`](https://w3c.github.io/IndexedDB/#dom-idbdatabase-transaction) does. (That confused me until I hunted around in the spec and found the definitions of each.) – JamesTheAwesomeDude Jul 18 '22 at 19:01
  • 1
    "do you mean that db.close is called before onupgradeneeded returns?" Any time before the upgrade transaction commits. So you could issue a request in the upgradeneeded callback, and call close() before or when that request's success callback fires. The upgrade transaction is like other transactions - it'll stay alive as long as you keep issuing requests against it, and try to commit once it has no more outstanding requests. This probably isn't useful in the context of the original question, though, which is trying to "cancel" the open as quickly as possible. – Joshua Bell Jul 18 '22 at 20:21