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

为何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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 05:27:18