4

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;
}
2
  • 2
    Possible duplicate of Wait until all ES6 promises complete, even rejected promises Commented Jun 3, 2018 at 23:03
  • 1
    await waitFor(50) why the small pause? sounds like you're not waiting for an asynchronous operation to finish, rather, crossing your fingers and hoping you've waited long enough Commented Jun 3, 2018 at 23:08

2 Answers 2

7

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)

Sign up to request clarification or add additional context in comments.

3 Comments

I'm sorry, what exactly should I change in my toDataURL? I've added it to my question
The snippet I posted passes a Promise's resolve into the callback-based toDataURL, allowing it to be awaited, so you don't need to change toDataURL with this.
I've tried the first method, but now the entire array is empty :/
2

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;
  });
}
  

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.