jakearchibald / idb

IndexedDB, but with promises
https://www.npmjs.com/package/idb
ISC License
6.29k stars 353 forks source link

Can't reject the opening promise when getting a "blocked" event #186

Closed julienw closed 3 years ago

julienw commented 4 years ago

This is what I'd like to do:

  const db = await openDB(DATABASE_NAME, DATABASE_VERSION, {
    upgrade(db) {
      db.createObjectStore(OBJECTSTORE_NAME, {
        // ...
      });
    },
    blocked() {
      throw new Error('some message explaining to close other tabs');
    },
  });

Then the error is propagated to the caller which makes it easy to catch and handle.

In some raw IDB code, this could look like this:

  const openReq = indexedDB.open(DATABASE_NAME);
  return new Promise((resolve, reject) => {
    openReq.onsuccess = () => resolve(openReq.result);
    openReq.onblocked = () => {
      reject(new Error('some message explaining to close other tabs'));
    };
  });

This takes advantage that success won't ever happen before blocked.

And this is too bad that we can't do the same with idb.

I'm not sure how this would fit in the code though. Maybe something like this:

export function openDB<DBTypes extends DBSchema | unknown = unknown>(
  name: string,
  version: number,
  { blocked, upgrade, blocking, terminated }: OpenDBCallbacks<DBTypes> = {},
): Promise<IDBPDatabase<DBTypes>> {
  const request = indexedDB.open(name, version);
  const openPromise = wrap(request) as Promise<IDBPDatabase<DBTypes>>;

  if (upgrade) {
    ...
  }

  const resultPromise = new Promise((resolve, reject) => {
    if (blocked) {
      request.addEventListener('blocked', () => {
        try { 
          blocked();
          resolve();
        } catch(e) {
          reject(e);
        }
      });
    }
  });

  openPromise
    .then((db) => {
      ...
    })
    .catch(() => {});

  return Promise.all(openPromise, resultPromise);
}

What do you think?

jakearchibald commented 4 years ago

Does this work?

function openButRejectOnBlocking(name, version, upgrade) {
  return new Promise((resolve, reject) => {
    openDB(name, version, {
      upgrade,
      blocked() {
        reject(Error('Blocked'));
      },
    }).then(resolve, reject);
  });
}
julienw commented 4 years ago

I believe this would work! Looking at this with fresh eyes, rejecting the promise early would prevent it from being resolved later when the user actually closes other tabs...