0

After reading How do I return the response from an asynchronous call? by Felix Kling, I am still confused about how I can return a value from an asynchronous callback.

My goal: convert a static image to base64 once and store that image in indexDB until indexDB throws some kind of storage error.

I am using this async idb npm module

// init the idb store
const initIDB = async () => {

  const db = await openDB('db', 1, {
    upgrade(db) {
      db.createObjectStore('tempStore', { keyPath: 'id', autoIncrement: true });
    },
  });
  const tx = db.transaction('tempStore', 'readwrite');
  await overloadIDB(tx.store);
  await tx.done;


  return true;
};
// random number generator
const getRandomArbitrary = (min, max) => Math.random() * (max - min) + min;

// function will overload the idb
const overloadIDB = async (store) => {
  const imgurl = "someLocalImage.png";
  const promises = [];
  return toDataURL(imgurl, async (s) => {
    for (let i = 0; i < 10; i++) {
      if (i > 0 && i % 100 === 0) console.log('A set done');
      try {
        const num = Math.round(getRandomArbitrary(1, 1000000));
        const data = {
          id: num,
          img: s,
        };
        store.add(data);
      } catch (e) {
        console.log(e.toString());
        console.dir(e);
        break;
      }
    }
    console.log('Done');
  });
};

// convert image to base64
const toDataURL = (url, callback) => {
  const xhr = new XMLHttpRequest();
  xhr.onload = () => {
    const reader = new FileReader();
    reader.onloadend = () => {
      callback(reader.result);
    };
    reader.readAsDataURL(xhr.response);
  };
  xhr.open('GET', url);
  xhr.responseType = 'blob';
  xhr.send();
};

Ideally, I would like to return the value from the toDataURL's callback function and use that result in the for loop but I always get undefined which makes sense due to asynchronous behaviour.

The above code fails to execute the transaction store.add(data) multiple times and fails when i = 0.

I have tried wrapping toDataURL with a new Promise(resolve, reject) like so

const toDataURL = (url, callback) => new Promise((resolve, reject) => {
  const xhr = new XMLHttpRequest();
  xhr.open('GET', url);
  xhr.responseType = 'blob';
  xhr.onload = () => {
    const reader = new FileReader();
    reader.onloadend = () => {
      resolve(callback(reader.result));
    };
    reader.readAsDataURL(xhr.response);
  };
  xhr.send();
});

and then using Promise.all to resolve an array of stores like so

const overloadIDB = async (store) => {
  const imgurl = 'someLocalImage.png';
  const promises = [];
  return toDataURL(imgurl, async (s) => {
    console.log('s :', s);
    for (let i = 0; i < 10; i++) {
      if (i > 0 && i % 100 === 0) console.log('A set done');
      try {
        const num = Math.round(getRandomArbitrary(1, 1000000));
        const data = {
          id: num,
          img: s,
        };
        promises.push(store.add(data));
      } catch (e) {
        console.log(e.toString());
        console.dir(e);
        break;
      }
    }
    await Promise.all(promises);
    console.log('Done');
  });
};

but returns an error Failed to execute 'add' on 'IDBObjectStore': The transaction has finished.

At this point I think I my approach is flawed but I am not sure how I can fix it. Can anyone point to some solution please?

asus
  • 1,427
  • 3
  • 25
  • 59
  • How does overloadIDB get the store? It sounds like the store is your problem; it has closed the transaction when you're trying to make a new transaction. Does it do that on the first time through the for loop or on the second? Does store.add return a promise? – RTS Oct 28 '19 at 19:26
  • the store is passed as an arg to overloadIDB. I believe it closes the transaction on the first time since `i` never gets incremented. and yes, store.add returns a Promise – asus Oct 28 '19 at 19:33

1 Answers1

0

You cannot perform async operations in the middle of indexedDB operations. Perform your fetch entirely, then connect, create a transaction, and store the result.

Josh
  • 17,834
  • 7
  • 50
  • 68