为何JavaScript中await的convertToPng函数未按预期顺序执行?
异步函数await后执行顺序不符合预期问题
我已使用await等待convertToPng函数执行完成(直至触发imgOnLoad事件并完成PDF创建),但该函数仍与后续代码同步执行,执行顺序不符合预期。
实际执行日志顺序
- "Convert to png function has ended"
- "Proceeding after convert to png"
- "SAVING PDF INTO COMPUTER"
- "imgOnLoad event entered"
- "ATTACHING IMAGE 1"
预期执行日志顺序
- "imgOnLoad event entered"
- "ATTACHING IMAGE 1"
- "Convert to png function has ended"
- "Proceeding after convert to png"
- "SAVING PDF INTO COMPUTER"
简化代码
for (let i = 0; i < selected_views.length; i++) { $('.loading-text').html("Exporting..."); $('.loading-count').html((i + 1) + "/" + selected_views.length); let node = document.getElementsByClassName('view-container view-grid-' + view_id[1])[0]; const data = await convertToPng(doc, node, doc_width, doc_height, doc_wh_ratio); console.log("Proceeding after convert to png"); } $('.loading-text').html("Loading..."); $("#loading").hide(); console.log("SAVING PDF INTO COMPUTER"); doc.save(dashboard_name + '_' + client_name + '.pdf'); async function convertToPng(doc, page, doc_width, doc_height, doc_wh_ratio) { // window.html2canvas = html2canvas; // Go to next page doc.addPage(); let chartNodes = page.childNodes; for(let i = 0; i < chartNodes.length; i++){ console.log(chartNodes[i]); console.log(chartNodes[i].firstChild.firstChild); if(chartNodes[i].firstChild.firstChild.tagName == 'TABLE'){ (doc.autoTable({ html: chartNodes[i].firstChild.firstChild })); } else{ let options = { "cacheBust":true } let dataUrl = await domtoimage.toPng(page, options); console.log('~~~~~~~~~'); console.log(dataUrl); console.log('~~~~~~~~~'); img.src = dataUrl; let img = new Image(); img.onload = function() { console.log("imgOnLoad event entered"); img_wh_ratio = img.width / img.height; if (img_wh_ratio > doc_wh_ratio) { console.log("ATTACHING IMAGE 1"); // Add dashboard image to pdf with margins doc.addImage(dataUrl, 'PNG', 2, 2, doc_width - 4, 0); // Set width as limiting dimension } else { console.log("ATTACHING IMAGE 2"); // Get margins to centralize horizontally let new_width = img.width * doc_height / img.height; let total_margin = doc_width - new_width; let side_margin = total_margin / 2; // Add dashboard image to pdf with margins doc.addImage(dataUrl, 'PNG', side_margin + 2, 2, 0, doc_height - 4); // Set height as limiting dimension } } } } console.log("Convert to png function has ended"); }
问题原因及解决方案
核心问题是convertToPng里的img.onload是异步回调,但没有被包装成Promise,await只能等待domtoimage.toPng完成,之后函数直接执行到结束,而img.onload会在后续触发,导致顺序混乱。同时原代码存在img变量提前使用的错误。
修改方案
把图片加载和PDF处理逻辑包装成Promise,让await能等待这部分异步操作完成,同时修正变量声明顺序:
async function convertToPng(doc, page, doc_width, doc_height, doc_wh_ratio) { doc.addPage(); let chartNodes = page.childNodes; for(let i = 0; i < chartNodes.length; i++){ console.log(chartNodes[i]); console.log(chartNodes[i].firstChild.firstChild); if(chartNodes[i].firstChild.firstChild.tagName == 'TABLE'){ doc.autoTable({ html: chartNodes[i].firstChild.firstChild }); } else{ let options = { "cacheBust": true } let dataUrl = await domtoimage.toPng(page, options); console.log('~~~~~~~~~'); console.log(dataUrl); console.log('~~~~~~~~~'); // 用Promise包裹图片加载逻辑,让await等待操作完成 await new Promise((resolve) => { let img = new Image(); img.src = dataUrl; img.onload = function() { console.log("imgOnLoad event entered"); const img_wh_ratio = img.width / img.height; if (img_wh_ratio > doc_wh_ratio) { console.log("ATTACHING IMAGE 1"); doc.addImage(dataUrl, 'PNG', 2, 2, doc_width - 4, 0); } else { console.log("ATTACHING IMAGE 2"); const new_width = img.width * doc_height / img.height; const total_margin = doc_width - new_width; const side_margin = total_margin / 2; doc.addImage(dataUrl, 'PNG', side_margin + 2, 2, 0, doc_height - 4); } resolve(); // 处理完成后标记Promise完成 } // 添加错误处理,避免Promise一直pending img.onerror = function(err) { console.error("图片加载失败", err); resolve(); } }); } } console.log("Convert to png function has ended"); }
效果说明
修改后,convertToPng会等待每张图片加载完成并处理完PDF添加操作后才结束,后续的Proceeding after convert to png和PDF保存代码会严格按预期顺序执行。
内容的提问来源于stack exchange,提问作者Yeo Bryan
相关产品推荐
相关产品推荐

