首页 > 解决方案 > JavaScript:使用 Promises 按顺序加载图像时出现奇怪的错误

问题描述

我有一组图像 url,我想按照它们在数组中的相应 URL 的放置顺序在网页上显示这些图像。

例如,我们有

const imgUrls = [
  "https://picsum.photos/400/400",
  "https://picsum.photos/200/200",
  "https://picsum.photos/300/300"
];

在这种情况下,我们400/400要先显示,然后再200/200显示300/300

如果我们天真地实现它,则无法保证顺序。

function loadImages(imgUrls, root) {
  imgUrls.forEach((url) => {
    const image = document.createElement("img");
    image.onload = () => {
      root.append(image);
    };
    image.src = url;
  });
}

所以我使用 Promises 来管理异步流控制

async function loadImagesInOrder(imgUrls, rootEl) {
  const promises = imgUrls.map(
    (url) =>
      new Promise((resolve, reject) => {
        const image = document.createElement("img");
        image.onload = resolve;
        image.onerror = reject;
        image.src = url;
      })
  );

  for (const p of promises) {
    const { currentTarget } = await p;
    rootEl.append(currentTarget);
  }
}

它有效,但并非一直有效。通过这种实现,有时会出现一些图像,null因此您最终会出现null在网页上。

这是现场演示 https://codesandbox.io/s/load-images-in-order-t16hs?file=/src/index.js

有人可以指出问题所在吗?还有没有更好的方法来确保图像按照数组中 URL 的顺序出现在页面上?

标签: javascripthtmles6-promise

解决方案


看起来.currentTarget事件的发生有时是null出于某种原因。一个简单的解决方法是使用图像本身解决 Promise,而不是通过有问题的load处理程序。基思在评论中找到了原因:

注意: event.currentTarget 的值仅在处理事件时可用

但是当你这样做时

  for (const p of promises) {
    const { currentTarget } = await p;
    rootEl.append(currentTarget);
  }

所有图像的加载过程都会立即启动,但它们是异步加载的,并且通常不会按顺序加载。如果 Promise 在图像已经加载后解析,.currentTarget则不会存在 - 例如,如果图像 1,然后图像 3 加载,然后图像 2 加载,图像 3 将在第三个图像的时间已经加载

const { currentTarget } = await p;

运行。

如果您最终只需要正确排序图像,更简单的方法是立即附加它们。

const root = document.querySelector("#app");

const imgUrls = [
  "https://picsum.photos/400/400",
  "https://picsum.photos/200/200",
  "https://picsum.photos/300/300"
];

for (const src of imgUrls) {
  root.appendChild(document.createElement('img')).src = src;
}
<div id="app">


推荐阅读