VolkovLabs / volkovlabs-dynamictext-panel

Business Text Panel for @grafana
https://docs.volkovlabs.io
Apache License 2.0
78 stars 14 forks source link

Sometimes slow performance #295

Closed craftzneko closed 4 weeks ago

craftzneko commented 3 months ago

At first i thought it was my JS in side the form but when creating a new form, it will sometimes cause the entire tab to go very slow. Closing the tab reopening and repeating the exact same steps makes it work normally. Profiling in Edge Dev tools doesnt show anything taking long to complete on main process. Normal machine processes are running fine, including all other tabs in browser open at the same time. Struggling to find the cause other than its only when using form panel and only occasionally. Any help to try and track down would be great. Cheers

craftzneko commented 3 months ago

I think i understand why its running slow, a docready function is running every 100ms. This doesnt seem to happen all the time. My dashboard is complex, it has a form panel and the JS docready function is in a dynamic text panel. This panel has the following JS

function docReady(fn) {
  if (document.readyState === "complete" || document.readyState === "interactive") {
    setTimeout(fn, 1);
  } else {
    document.addEventListener("DOMContentLoaded", fn);
  }
}

// JavaScript
var downloadCSV = function () {
  // Assuming `data` is your object
  // Get the keys from the first item, excluding those ending with 'json' except 'summaryjson'
  let headers = Object.keys(data.data[0]).filter(key => !key.endsWith('json') || key === 'summaryjson');
  let summaryKeys = Object.keys(JSON.parse(data.data[0].summaryjson || '{}'));
  headers = headers.filter(header => header !== 'summaryjson' && header !== 'statusColor' && header !== 'agentver' && header !== 'ComputerName' && header !== 'AzureDeviceID').concat(summaryKeys);

  // Create CSV string
  let csvStr = headers.join(',') + '\n';  // Join headers to make first row

  // Now we add the values for each field
  data.data.forEach(item => {
    let rowStr = '';
    headers.forEach(header => {
      let value;
      if (header in item && header !== 'summaryjson') {
        value = item[header];
        if (header === 'datecreated') { // assuming 'datecreated' is your date field
          value = new Date(value).toISOString(); // convert to ISO string
        }
      } else if (item.summaryjson && header in JSON.parse(item.summaryjson)) {
        value = JSON.parse(item.summaryjson)[header];
      }
      value = `"${value || ''}"`;  // Wrap in double quotes
      rowStr += value + ',';
    });
    csvStr += rowStr.slice(0, -1) + '\n';  // Remove trailing comma and add newline for next row
  });

  // Create a Blob from the CSV string
  let csvBlob = new Blob([csvStr], { type: 'text/csv;charset=utf-8;' });

  // Create a download link
  let downloadLink = document.createElement('a');
  let url = URL.createObjectURL(csvBlob);
  downloadLink.href = url;
  downloadLink.download = 'data.csv';

  // Create a new invisible iframe and set the download link as its source
  let iframe = document.createElement('iframe');
  iframe.style.display = 'none';
  document.body.appendChild(iframe);
  iframe.src = downloadLink.href;

  // Remove the iframe after the download starts
  setTimeout(function () {
    document.body.removeChild(iframe);
  }, 100);
};

docReady(function () {
  console.log('docReady function is being run');

  // Get the current value of the SearchQuery variable
  const currentValue = replaceVariables('$SearchQuery');

  // Get the main div
  let mainDiv = document.querySelector('sqlcode');

  // Check if mainDiv exists and currentValue is empty
  if (mainDiv && currentValue === '') {
    // If it is, hide the main div
    mainDiv.style.display = 'none';
  } else if (mainDiv) {
    // If it's not, show the main div
    mainDiv.style.display = 'block';
  }
  var exportButtonAdded = false;

  var exportButton = document.querySelector('.fas.fa-file-export');
  if (exportButton) {
    // Remove any existing event listeners
    var clone = exportButton.cloneNode(true);
    exportButton.parentNode.replaceChild(clone, exportButton);

    // Add the event listener
    clone.addEventListener('click', downloadCSV);
  }
});

copyClip = async (element) => {
  var textToCopy = element.getAttribute('data-text');
  var icon = element;
  try {
    await navigator.clipboard.writeText(textToCopy);
    console.log('Copying to clipboard was successful!');
    icon.classList.remove("fa-copy");
    icon.classList.add("fa-check");
    setTimeout(function () {
      icon.classList.remove("fa-check");
      icon.classList.add("fa-copy");
    }, 1000); // switch back to the copy icon after 1 second
  } catch (err) {
    console.error('Could not copy text: ', err);
  }
}

// JavaScript
handlebars.registerHelper('count', function (array) {
  return array.length;
});
vitPinchuk commented 4 weeks ago

Hi, @craftzneko Are you still having difficulties with this open question ? Was the slow perfomance related to the plugin, or the slow performance related to docReady function? Thanks

mikhail-vl commented 4 weeks ago

@craftzneko We looked at your code with @vitPinchuk and found multiple setTimeout() functions with parameters from 1 to 1000ms, which definitely slow down the process.

Please update logic in the code to resolve performance issues.

craftzneko commented 3 weeks ago

Hi thanks @mikhail-vl I am new to learning JS and i tend to just jump straight in. With that said the problem no longer happens and i have been making changes to the code so possibly my understanding has grown and i corrected the issue.

mikhail-vl commented 3 weeks ago

@craftzneko Let us know if you would like to share your experience with community in the blog post.