Reputation: 189
I am trying to get the value of some array elements. It works for the elements [0], [1], [2], [3], but not [4].
function getBase64() {
const urls = ['https://i.imgur.com/egNg7JU.jpg',
'https://i.imgur.com/RLZ7WH1.jpg', 'https://i.imgur.com/qfabBbA.jpg',
'https://i.imgur.com/Zuh1KaX.jpg', 'https://i.imgur.com/yD7X6Q1.jpg'
];
let base64urls = [];
const start = async () => {
await asyncForEach(urls, async (num) => {
await waitFor(50)
toDataURL(num, function(dataURL) {
base64urls.push(dataURL);
});
})
console.log(base64urls);
console.log(base64urls[4]);
}
start()
}
async function asyncForEach(array, callback) {
for (let index = 0; index < array.length; index++) {
await callback(array[index], index, array)
}
}
const waitFor = (ms) => new Promise(r => setTimeout(r, ms))
toDataURL
simply returns the base64 value of an image. Whenever I try console.log(base64urls[4])
, it returns 'undefined'. I do get the correct value for the previous elements. Is there some way to restructure this, or perhaps use a different method of waiting for the array to completely populate before checking for the values of its elements?
EDIT
Here is my toDataURL
function toDataURL(src, callback) {
const image = new Image();
image.crossOrigin = 'Anonymous';
image.onload = function () {
const canvas = document.createElement('canvas');
const context = canvas.getContext('2d');
canvas.height = this.naturalHeight;
canvas.width = this.naturalWidth;
context.drawImage(this, 0, 0);
const dataURL = canvas.toDataURL('image/jpeg');
callback(dataURL);
};
image.src = src;
}
Upvotes: 4
Views: 9382
Reputation: 24324
You can use promise.all for this kind of situation to wait for the results of your queries
const urls = ['https://i.imgur.com/egNg7JU.jpg',
'https://i.imgur.com/RLZ7WH1.jpg', 'https://i.imgur.com/qfabBbA.jpg',
'https://i.imgur.com/Zuh1KaX.jpg', 'https://i.imgur.com/yD7X6Q1.jpg'];
let base64urls = [];
Promise.all(urls.map(url => fetch(url))).then(res => toBase64DataURL(res)).then(result => {base64urls.push(result.toDataURL());
console.log(base64urls);});
function toBase64DataURL(src) {
return new Promise(resolve => {
const image = new Image();
image.crossOrigin = 'Anonymous';
image.onload = _=> {
const canvas = document.createElement('canvas');
const context = canvas.getContext('2d');
canvas.height = this.naturalHeight;
canvas.width = this.naturalWidth;
context.drawImage(this, 0, 0);
const dataURL = canvas.toDataURL('image/jpeg');
resolve(dataURL);
};
image.src = src;
});
}
Upvotes: 2
Reputation: 371198
It looks like toDataURL
is asynchronous and callback-based - either change it so that it returns a Promise
and await
that Promise
, or pass a Promise
's resolve
into the callback:
async function getBase64() {
const urls = ['https://i.imgur.com/egNg7JU.jpg',
'https://i.imgur.com/RLZ7WH1.jpg', 'https://i.imgur.com/qfabBbA.jpg',
'https://i.imgur.com/Zuh1KaX.jpg', 'https://i.imgur.com/yD7X6Q1.jpg'];
const base64urls = [];
for (const url of urls) {
const dataURL = await new Promise(resolve => toDataURL(url, resolve));
base64urls.push(dataURL);
}
console.log(base64urls);
console.log(base64urls[4]);
}
If you want to change your toDataURL
function to return a Promise so you don't have to treat it like a callback:
function toDataURL(src) {
return new Promise(resolve => {
const image = new Image();
image.crossOrigin = 'Anonymous';
image.onload = function () {
const canvas = document.createElement('canvas');
const context = canvas.getContext('2d');
canvas.height = this.naturalHeight;
canvas.width = this.naturalWidth;
context.drawImage(this, 0, 0);
const dataURL = canvas.toDataURL('image/jpeg');
resolve(dataURL);
};
image.src = src;
});
}
and then const dataURL = await toDataURL(url)
Upvotes: 7