0

Why is the function convertToPng that I have stated to await for until completion, specifically until the imgOnLoad is entered and the PDF is created, is still being executed simultaneously?

Actual Results

enter image description here

  1. "Convert to png function has ended"
  2. "Proceeding after convert to png"
  3. "Saving PDF INTO COMPUTER"
  4. "imgOnLoad event entered"
  5. "ATTACHING IMAGE 1"

Expected Result

  1. "imgOnLoad event entered"
  2. "ATTACHING IMAGE 1"
  3. "Convert to png function has ended"
  4. "Proceeding after convert to png"
  5. "Saving PDF INTO COMPUTER"

Simplified Code

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");
        }
Yeo Bryan
  • 331
  • 4
  • 24

1 Answers1

1

That is because the onload event inside your convertToPng() function is happening asynchronous. By itself it does not return a promise so you need to put the loading procedure in another function which returns a promise alongside the image.

This way you can use the await keyword and await the actual image result - img = await loadImage(dataUrl);.

For example:

function loadImage(path) {
  return new Promise((resolve, reject) => {
    const img = new Image();
    img.src = path;
    img.onload = () => {
      resolve(img);
    }
    img.onerror = e => {
      reject(e);
    }
  });
}
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('~~~~~~~~~');
      let img = await loadImage(dataUrl);
      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");
}
obscure
  • 11,916
  • 2
  • 17
  • 36
  • Thanks it works!! But do you mind elaboring a bit more on why I cant await img.onload? – Yeo Bryan Jul 19 '22 at 14:45
  • No problem Yeo - glad I could help! =) The `await` keyword's solely purpose is 'awaiting' the result of a `Promise`. The `onload` event however is just an event that eventually gets dispatched and it does not ever resolve a Promise by itself. So `await img.onload` won't ever resolve (or reject if something fails) as it never returns a Promise. – obscure Jul 19 '22 at 14:54
  • In that case shouldnt the function code just stop running rather than continuing to execute code beyond this point as it should be waiting for a promise that never resolves? I also tried wrapping the onload event within a promise itself and will resolve(true) when the onload event is entered and the image is added to the pdf. However this didn't work doesn't work as well? – Yeo Bryan Jul 19 '22 at 15:21