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?