如何在保留图片加载进度条更新逻辑的同时,使用Promise.all等待所有图片加载完成执行后续操作?
如何在保留图片加载进度条更新逻辑的同时,使用Promise.all等待所有图片加载完成执行后续操作?
我完全理解你的困扰——你既要通过图片的onload事件更新进度条,又要依赖Promise.all等待所有图片加载完成后生成PDF,但现在这两个逻辑因为onload被覆盖而冲突了。别担心,我们只需要调整事件绑定的逻辑,让两者可以共存,下面给你两种可行的解决方案:
问题根源
JavaScript中DOM元素的onload是一个单一函数引用,如果你先后给它赋值不同的函数,后赋值的会直接覆盖前一个。你原来的代码里,先给img.onload绑定了进度条更新的函数,后续Promise.all里又把img.onload替换成了Promise的resolve函数,导致进度条的逻辑被完全丢弃,这就是为什么两者不能同时工作的原因。
方案一:兼容原有代码结构,保留双逻辑
我们可以在创建Promise时,先保存原来的onload函数,在新的onload回调中先执行进度条更新,再resolve Promise,这样就不会覆盖原有逻辑:
// 1. 图片数组初始化与进度条绑定 let images = new Array(pageList.length); pageList.forEach((e, idx, arr) => { let i = new Image; i.crossOrigin = ""; // 保留原有进度条更新逻辑 i.onload = () => { progs[progs.length-1].value += 1; }; i.src = e.url + '&w=1280'; images[idx] = i; }); // 2. 修正后的Promise.all逻辑 Promise.all(images.map(img => { // 处理图片已经加载完成的边界情况 if (img.complete) { // 补全进度条更新 progs[progs.length-1].value += 1; return Promise.resolve(); } return new Promise(resolve => { // 保存原来的进度条更新函数 const originalOnLoad = img.onload; // 新的onload:先执行进度条更新,再resolve Promise img.onload = () => { if (originalOnLoad) originalOnLoad(); resolve(); }; // 错误情况下也resolve(你也可以选择reject来处理加载失败) img.onerror = resolve; }); })).then(() => { // 后续PDF生成逻辑保持不变 images.forEach((i) => { let dpi = i.height/dimensions[1]; pdf.addPage([Math.round(i.width/dpi * 100) / 100, dimensions[1]], 'p'); pdf.addImage(i, 'JPEG', 0, 0, Math.round(i.width/dpi * 100) / 100, dimensions[1]); }); }) .then(() => { // 这里可以添加PDF保存或者其他后续操作 // pdf.save('output.pdf'); });
方案二:从一开始就用Promise封装图片加载(更优雅)
这种方式会把图片加载、进度条更新和Promise resolve逻辑完全整合在一起,从根源上避免覆盖问题,代码结构也更清晰,是Promise的最佳实践:
const promises = pageList.map((e) => { return new Promise((resolve, reject) => { const img = new Image(); img.crossOrigin = ""; img.onload = () => { // 加载完成时同时更新进度条并resolve图片对象 progs[progs.length-1].value += 1; resolve(img); }; // 可选:处理图片加载失败的情况 img.onerror = (err) => { console.error(`图片加载失败: ${e.url}`, err); reject(err); }; img.src = e.url + '&w=1280'; }); }); // 等待所有图片加载完成 Promise.all(promises) .then((loadedImages) => { // loadedImages就是所有加载成功的图片数组 loadedImages.forEach((i) => { let dpi = i.height/dimensions[1]; pdf.addPage([Math.round(i.width/dpi * 100) / 100, dimensions[1]], 'p'); pdf.addImage(i, 'JPEG', 0, 0, Math.round(i.width/dpi * 100) / 100, dimensions[1]); }); }) .catch((err) => { // 统一处理加载错误 alert('部分图片加载失败,无法生成PDF'); }) .then(() => { // 后续操作,比如保存PDF // pdf.save('output.pdf'); });
关键说明
- 方案一适合你想尽量保留原有代码结构的场景,核心是通过保存原有
onload函数避免覆盖; - 方案二更推荐:把每个异步操作(图片加载)从一开始就封装成Promise,所有相关逻辑(进度条、resolve)都在同一个
onload回调中处理,逻辑更集中,也更不容易出错; - 不要忽略错误处理:方案二中添加了
onerror的reject逻辑,你可以根据需求决定是忽略失败的图片还是终止流程。
内容来源于stack exchange
相关产品推荐
相关产品推荐

