JavaScript - 等待数组填充完成

3
我试图获取一些数组元素的值。对于元素[0]、[1]、[2]、[3]可以正常工作,但是对于[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 简单地返回图像的 base64 值。每当我尝试 console.log(base64urls[4]) 时,它会返回 'undefined'。我确实获得了前面元素的正确值。是否有一些重构的方式,或者使用不同的方法来等待数组完全填充后再检查其元素的值?

编辑

这是我的 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
可能是[等待所有ES6 promise完成,即使是拒绝的promise]的重复问题(https://dev59.com/o10Z5IYBdhLWcg3wdQIy)。 - Get Off My Lawn
1
await waitFor(50) 为什么要加一个短暂的暂停?听起来你并没有等待异步操作完成,而是在盲目地猜测等待足够的时间。 - Jaromanda X
2个回答

6

看起来toDataURL是异步的且基于回调的——要么更改它,使其返回一个Promise并使用await等待该Promise,要么将Promiseresolve传递给回调函数:

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

如果您想将toDataURL函数更改为返回Promise,以便您不必像处理回调函数一样进行处理:

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

然后执行const dataURL = await toDataURL(url)代码


对不起,请问我应该在我的 toDataURL 中更改什么?我已经将它添加到我的问题中了。 - HackNode
我发布的代码片段将Promiseresolve传递到基于回调的toDataURL中,使其可以被await,因此您无需更改此toDataURL - CertainPerformance
我尝试了第一种方法,但现在整个数组都是空的 :/ - HackNode

2
您可以使用 Promise.all 来等待查询结果。它适用于这种情况。

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接