Search code examples
javascriptexceljs

JavaScript asynchronous function executing too many times


The function below is inside another function called calculate(). The calculate function is triggered every time I press a specific button.

The problem is for every time I am clicking that button the function below is generating multiple files and when I click to download, all the files generated are downloaded, which is not supposed to happen. It should only download the last generated file.

function CalculateMedia() {
  // Defining every var to retrieve the values from inputs
  let TrienalHolder = document.getElementById('trienal');
  let trienal = (TrienalHolder.options[TrienalHolder.selectedIndex]).textContent;

  let BienalIHolder = document.getElementById('bienal_i');
  let bienal_i = (BienalIHolder.options[BienalIHolder.selectedIndex]).textContent;

  let BienalIIHolder = document.getElementById('bienal_ii');
  let bienal_ii = (BienalIIHolder.options[BienalIIHolder.selectedIndex]).textContent;

  // Média de cada disciplina por ano, Provas de Ingresso, Peso PI e Peso Média
  let pt10 = document.getElementById("pt_10").value;
  let pt11 = document.getElementById("pt_11").value;
  let pt12 = document.getElementById("pt_12").value;

  let le10 = document.getElementById("le_10").value;
  let le11 = document.getElementById("le_11").value;

  let fil10 = document.getElementById("fil_10").value;
  let fil11 = document.getElementById("fil_11").value;

  let trienal10 = document.getElementById("trienal_10").value;
  let trienal11 = document.getElementById("trienal_11").value;
  let trienal12 = document.getElementById("trienal_12").value;

  let bienali10 = document.getElementById("bienal_i_10").value;
  let bienali11 = document.getElementById("bienal_i_11").value;

  let bienalii10 = document.getElementById("bienal_ii_10").value;
  let bienalii11 = document.getElementById("bienal_ii_11").value;

  let edf10 = document.getElementById("edf_10").value;
  let edf11 = document.getElementById("edf_11").value;
  let edf12 = document.getElementById("edf_12").value;

  let anuali = document.getElementById("anual_i").value;

  let anualii = document.getElementById("anual_ii").value;

  let Pi1 = document.getElementById("pi_pt").value;
  let Pi2 = document.getElementById("pi_le").value;
  let Pi3 = document.getElementById("pi_fil").value;
  let Pi4 = document.getElementById("pi_trienal").value;
  let Pi5 = document.getElementById("pi_bienal_i").value;
  let Pi6 = document.getElementById("pi_bienal_ii").value;

  let PPI = document.getElementById("peso_pi").value;


  //Média Final de cada disciplina
  let pt_final = (parseFloat(pt10) + parseFloat(pt11) + parseFloat(pt12)) / 3;
  let le_final = (parseFloat(le10) + parseFloat(le11)) / 2;
  let fil_final = (parseFloat(fil10) + parseFloat(fil11)) / 2;
  let trienal_final = (parseFloat(trienal10) + parseFloat(trienal11) + parseFloat(trienal12)) / 3;
  let bienal_i_final = (parseFloat(bienali10) + parseFloat(bienali11)) / 2;
  let bienal_ii_final = (parseFloat(bienalii10) + parseFloat(bienalii11)) / 2;
  let edf_final = (parseFloat(edf10) + parseFloat(edf11) + parseFloat(edf12)) / 3;
  let anual_i_final = parseFloat(anuali);
  let anual_ii_final = parseFloat(anualii);

  let PiPt = (parseFloat(Pi1)) / 10;
  let PiLe = (parseFloat(Pi2)) / 10;
  let PiFil = (parseFloat(Pi3)) / 10;
  let PiTrienal = (parseFloat(Pi4)) / 10;
  let PiBienali = (parseFloat(Pi5)) / 10;
  let PiBienalii = (parseFloat(Pi6)) / 10;

  let PesoProvasIngresso = (parseFloat(PPI)) / 100;
  let PesoMediaFinal = 1 - PesoProvasIngresso;

  let MediaFinalSecundario = ((pt_final + le_final + fil_final + trienal_final + bienal_i_final + bienal_ii_final + edf_final + anual_i_final + anual_ii_final) / 9).toFixed(2);

  //This part of the code puts the PI into an array and only returns the not NaN after that it calculates the average
  let piArray = [PiPt, PiLe, PiFil, PiTrienal, PiBienali, PiBienalii];
  let validPiArray = piArray.filter(function(value) {
    return !isNaN(value);
  });

  let MediaProvasIngresso = validPiArray.length > 0 ? validPiArray.reduce(function(acc, value) {
    return acc + value;
  }, 0) / validPiArray.length : 0;


  let MediaFinalAcessoES = ((MediaFinalSecundario * PesoMediaFinal) + (MediaProvasIngresso * PesoProvasIngresso)).toFixed(2);

  let checkboxes = document.querySelectorAll("[id^='check_']");
  let atLeastOneChecked = false;

  for (let i = 0; i < checkboxes.length; i++) {
    if (checkboxes[i].checked) {
      atLeastOneChecked = true;
      break;
    }
  }

  if (atLeastOneChecked) {
    document.getElementById("media-secundarioES").textContent = `${MediaFinalSecundario}`;
    document.getElementById("media-ES").textContent = `${MediaFinalAcessoES}`;
    $('#notafinalES').modal('show');
  } else {
    document.getElementById("media-secundario").textContent = `${MediaFinalSecundario}`;
    $('#notafinal').modal('show');
  }

  let excelFileDownloaded = false;

  // Export to excel function
  async function generateAndDownloadExcel(fillB19 = true) {
    if (excelFileDownloaded) {
      return;
    }

    // Load the template.xlsx file using fetch API
    const workbook = new ExcelJS.Workbook();
    const response = await fetch('ExcelTemplate.xlsx');
    const arrayBuffer = await response.arrayBuffer();

    // Read the Excel file from the Blob
    await workbook.xlsx.load(arrayBuffer);

    // Get the first sheet of the workbook
    const sheet = workbook.getWorksheet(1);

    // Update cell values
    sheet.getCell('B6').value = pt10;
    sheet.getCell('C6').value = pt11;
    sheet.getCell('D6').value = pt12;

    sheet.getCell('B7').value = le10;
    sheet.getCell('C7').value = le11;

    sheet.getCell('B8').value = fil10;
    sheet.getCell('C8').value = fil11;

    sheet.getCell('B9').value = trienal10;
    sheet.getCell('C9').value = trienal11;
    sheet.getCell('D9').value = trienal12;

    sheet.getCell('B10').value = bienali10;
    sheet.getCell('C10').value = bienali11;

    sheet.getCell('B11').value = bienalii10;
    sheet.getCell('C11').value = bienalii11;

    sheet.getCell('B12').value = edf10;
    sheet.getCell('C12').value = edf11;
    sheet.getCell('D12').value = edf12;

    sheet.getCell('D13').value = anuali;

    sheet.getCell('D14').value = anualii;

    sheet.getCell('E6').value = Pi1;
    sheet.getCell('E7').value = Pi2;
    sheet.getCell('E8').value = Pi3;
    sheet.getCell('E9').value = Pi4;
    sheet.getCell('E10').value = Pi5;
    sheet.getCell('E11').value = Pi6;

    sheet.getCell('B16').value = PPI;

    sheet.getCell('B18').value = MediaFinalSecundario;
    if (fillB19) {
      sheet.getCell('B19').value = MediaFinalAcessoES;
    }

    sheet.getCell('A9').value = trienal;
    sheet.getCell('A10').value = bienal_i;
    sheet.getCell('A11').value = bienal_ii;


    // Generate the Excel file and download it
    const buffer = await workbook.xlsx.writeBuffer();
    const blob = new Blob([buffer], {
      type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
    });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    let currentDate = new Date().toLocaleDateString().replaceAll('/', '-');
    let fileName = `MediaCalculada[${currentDate}].xlsx`;
    a.href = url;
    a.download = fileName;
    a.click();
    setTimeout(() => URL.revokeObjectURL(url), 1000);

    excelFileDownloaded = true;
  }

  const downloadButton = document.getElementById('downloadButton');
  const downloadButtonES = document.getElementById('downloadButtonES');

  downloadButton?.addEventListener('click', () => {
    generateAndDownloadExcel(false);
  });

  downloadButtonES?.addEventListener('click', () => {
    generateAndDownloadExcel();
  });
}

Solution

  • I would think you need to stop the function here:

      }
    } // end of CalculateMedia
    let excelFileDownloaded = false;
    

    and then delegate

      excelFileDownloaded = true;
    }
    document.querySelector(".someClassOfACommonStaticContainer')
      .addEventListener('click', (e) => {
      const tgt = e.target;
      if (tgt.matches("#downloadButton")) generateAndDownloadExcel(false);
      else if (tgt.matches("#downloadButtonES")) generateAndDownloadExcel();
    });
    

    or shorter:

    if (tgt.matches("id^=downloadButton")) 
      generateAndDownloadExcel(tgt.id==="downloadButtonES");