使用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
相关产品推荐
相关产品推荐

