You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用forEach与image.onload时如何保留数组顺序,解决异步乱序问题

问题原因

图片加载属于异步IO操作,forEach循环会一次性发起所有图片的加载请求,体积更小、网络响应更快的图片会先加载完成并触发onload回调,回调执行顺序和数组原始顺序没有关联,直接在onload中绘制到canvas就会出现结果错乱的问题。

注意:你原有代码中Object.entries(data)返回的每一项是[键名, 键值]格式的数组,如果你的brush属性存储在键值中,需要把element[0]['brush']调整为element[1]['brush']。

修复方案

方案1:并行加载后按原始顺序绘制(推荐)

所有图片同时发起加载请求,加载完成后按原始数组下标存储,全部加载完成后再按顺序统一绘制,总加载耗时等于最慢的单张图片加载耗时,性能更好。

// 提取所有图片路径,保留原始顺序
const imgSources = Object.entries(data).map(element => element[0]['brush']);
// 用于存储加载完成的图片,下标和原始顺序一一对应
const loadedImgs = new Array(imgSources.length);
// 统计已加载完成的图片数量
let loadedCount = 0;

imgSources.forEach((src, index) => {
  const img = new Image();
  img.onload = () => {
    loadedImgs[index] = img;
    loadedCount++;
    // 所有图片加载完成后按原始顺序绘制
    if (loadedCount === imgSources.length) {
      loadedImgs.forEach(img => {
        brushctx.drawImage(img, 0, 0);
      });
    }
  };
  // 加载失败兜底逻辑,可根据业务需求调整
  img.onerror = () => {
    loadedCount++;
    if (loadedCount === imgSources.length) {
      loadedImgs.forEach(img => {
        img && brushctx.drawImage(img, 0, 0);
      });
    }
  };
  img.src = src;
});

方案2:串行按顺序加载

加载完成前一张图片后,再发起下一张图片的加载请求,天然保证绘制顺序,逻辑更简单,适合图片数量少的场景。

// 提取所有图片路径,保留原始顺序
const imgSources = Object.entries(data).map(element => element[0]['brush']);
let currentLoadIndex = 0;

function loadNextImg() {
  // 所有图片加载完成则终止
  if (currentLoadIndex >= imgSources.length) return;
  const img = new Image();
  img.onload = () => {
    brushctx.drawImage(img, 0, 0);
    currentLoadIndex++;
    // 加载下一张
    loadNextImg();
  };
  img.src = imgSources[currentLoadIndex];
}

// 启动加载流程
loadNextImg();

内容的提问来源于stack exchange,提问作者Randima Lahiru

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.07 14:36:04