2

I'm writing a google docs apps script in making a google docs add-on. When the user clicks a button in the sidebar, an apps script function is called named executeSpellChecking. This apps script function makes a remote POST call after getting the document's text.

total time = time that takes from when user clicks the button, until the .withSuccessHandler(, that means until executeSpellChecking returns = 2000 ms

function time = time that takes for the executeSpellChecking call to complete from its start to its end = 1400 ms

t3 = time that takes for the remote POST call to be completed = 800ms

t4 = time that takes for the same remote POST call to complete in a VB.NET app = 200ms

Problems:

Why total time to complete is bigger than total function time by a staggering 600ms, what else happens there? shouldn't they be equal? How can I improve it?

Why t3 is bigger than t4 ? Shouldn't they be equal? Is there something wrong with POST requests when happening from .gs? How can I improve it ?

the code is (sidebar.html):

  function runSpellChecking() {
    gb_IsSpellcheckingRunning = true;
    //gb_isAutoCorrecting = false;
    gi_CorrectionCurrWordIndex = -1;
    $("#btnStartCorr").attr("disabled", true);

    $("#divMistakes").html("");
    this.disabled = true;
    //$('#error').remove();
    var origin = $('input[name=origin]:checked').val();
    var dest = $('input[name=dest]:checked').val();
    var savePrefs = $('#save-prefs').is(':checked');
    //var t1 = new Date().getTime();
    console.time("total time");
    google.script.run
        .withSuccessHandler(
          function(textAndTranslation, element) {
            if (gb_IsSpellCheckingEnabled) {
              console.timeEnd("total time");
              //var t2 = new Date().getTime();
              go_TextAndTranslation = JSON.parse(JSON.stringify(textAndTranslation));
              var pagewords = textAndTranslation.pagewords;
              var spellchecked = textAndTranslation.spellchecked;
              //alert("total time to complete:" + (t2-t1) + "###" + go_TextAndTranslation.time);
              //irrelevant code follows below...
            }
          })
        .withFailureHandler(
          function(msg, element) {
            showError(msg, $('#button-bar'));
            element.disabled = false;
          })
        .withUserObject(this)
        .executeSpellChecking(origin, dest, savePrefs);
  }

and the called function code is (spellcheck.gs):

function executeSpellChecking(origin, dest, savePrefs) {
  //var t1 = new Date().getTime();
  console.time("function time");
  var body = DocumentApp.getActiveDocument().getBody();
  var alltext = body.getText();
  var lastchar = alltext.slice(-1);
  if (lastchar != " " && lastchar != "\n") {
    body.editAsText().insertText(alltext.length, "\n");
    alltext = body.getText();
  }

  var arr_alltext = alltext.split(/[\s\n]/);
  var pagewords = new Object;
  var pagewordsOrig = new Object;
  var pagewordsOrigOffset = new Object;
  var offset = 0;
  var curWord = "";
  var cnt = 0;
  
  for (var i = 0; i < arr_alltext.length; i++) {
    curWord = arr_alltext[i];
    if (StringHasSimeioStiksis(curWord)) {
      curWord = replaceSimeiaStiksis(curWord);
      var arr3 = curWord.split(" ");
      for (var k = 0; k < arr3.length; k++) {
        curWord = arr3[k];
        pagewords["" + (cnt+1).toString()] = curWord.replace(/[`~@#$%^&*()_|+\-="<>\{\}\[\]\\\/]/gi, '');
        pagewordsOrig["" + (cnt+1).toString()] = curWord;
        pagewordsOrigOffset["" + (cnt+1).toString()] = offset;
        offset += curWord.length;
        cnt++;
      }
      offset++;
    } else {
      pagewords["" + (cnt+1).toString()] = curWord.replace(/[`~@#$%^&*()_|+\-="<>\{\}\[\]\\\/\n]/gi, '');
      pagewordsOrig["" + (cnt+1).toString()] = curWord;
      pagewordsOrigOffset["" + (cnt+1).toString()] = offset;
      offset += curWord.length + 1;
      cnt++;
    }
  }

  var respTString = "";

  var url = 'https://www.example.org/spellchecker.php';
  var data = {
    "Text" : JSON.stringify(pagewords),
    "idOffset" : "0",
    "lexID" : "8",
    "userEmail" : "test@example.org"
  };
  var payload = JSON.stringify(data);
  var options = {
    "method" : "POST",
    "contentType" : "application/json",
    "payload" : payload
  };
  //var t11 = new Date().getTime();
  console.time("POST time");
  var response = UrlFetchApp.fetch(url, options);
  console.timeEnd("POST time");
  //var t22 = new Date().getTime();
  var resp = response.getContentText();
  respTString = resp;
  var spellchecked = JSON.parse(respTString);

  var style = {};
  for (var k in pagewords){
      if (pagewords.hasOwnProperty(k)) {
          if (spellchecked.hasOwnProperty(k)) {
            if (spellchecked[k].substr(0, 1) == "1") {
              style[DocumentApp.Attribute.FOREGROUND_COLOR] = "#000000";
            }            
            if (spellchecked[k].substr(0, 1) == "0") {
              style[DocumentApp.Attribute.FOREGROUND_COLOR] = "#FF0000";
            }
            if (spellchecked[k].substr(0, 1) == "4") {
              style[DocumentApp.Attribute.FOREGROUND_COLOR] = "#0000FF";
            }
            if (pagewordsOrigOffset[k] < alltext.length) {
              body.editAsText().setAttributes(pagewordsOrigOffset[k], pagewordsOrigOffset[k] + pagewordsOrig[k].length, style);
            }
          }
      }
  }

  //var t2 = new Date().getTime();
  console.timeEnd("function time")
  return {
    "pagewords" : pagewords,
    "pagewordsOrig" : pagewordsOrig,
    "pagewordsOrigOffset" : pagewordsOrigOffset,
    "spellchecked" : spellchecked
  }
}

Thank you in advance for any help.

EDIT: I updated the code to use console.time according to the suggestion, the results are:

total time: 2048.001953125 ms
Jun 21, 2021, 3:01:40 PM    Debug   POST time: 809ms
Jun 21, 2021, 3:01:41 PM    Debug   function time: 1408ms

So the problem is not how time is measured. function time is 1400ms, while the time it takes to return is 2000ms, a difference of 600ms and the POST time is a staggering 800ms, instead of 200ms it takes in VB.net to make the exact same POST call.

MirrorMirror
  • 186
  • 8
  • 36
  • 70
  • 1
    I think you shouldn't use `Date()` to measure performances, read https://stackoverflow.com/a/15641427/1274485 – Yann39 Jun 19 '21 at 19:22
  • @Yann39 I updated the code to use console.time and timeEnd but that was not the problem. The results are the same. – MirrorMirror Jun 21 '21 at 12:20
  • 1
    It is normal that your "total time to complete" is bigger than your "total function time" because your `executeSpellChecking` is asynchronous, so the function may end before the callback completes. About the POST call taking longer than in VB.NET, it all depends on your VB.NET version, did you also use Google's `UrlFetchApp.fetch` ? Google's `fetch` may do some additional asynchronous processing, reordering, etc. so it may be slower than your VB.NET implementation. Best would be to provide a reproductible minimal example. – Yann39 Jun 21 '21 at 16:56

1 Answers1

0

Use console.time() and console.timeEnd(): https://developers.google.com/apps-script/reference/base/console

I modified the code for you. console.timeEnd() outputs the time duration in the console automatically, so I removed the alert for you that showed the time difference.

You might want the strings that I used as the parameter as some sort of constant variable, so there are no magic strings used twice. I hope this is of use to you.

function runSpellChecking() {
    gb_IsSpellcheckingRunning = true;
    //gb_isAutoCorrecting = false;
    gi_CorrectionCurrWordIndex = -1;
    $("#btnStartCorr").attr("disabled", true);

    $("#divMistakes").html("");
    this.disabled = true;
    //$('#error').remove();
    var origin = $('input[name=origin]:checked').val();
    var dest = $('input[name=dest]:checked').val();
    var savePrefs = $('#save-prefs').is(':checked');
    console.time("total time");
    google.script.run
        .withSuccessHandler(
          function(textAndTranslation, element) {
            if (gb_IsSpellCheckingEnabled) {
              console.timeEnd("total time");
              go_TextAndTranslation = JSON.parse(JSON.stringify(textAndTranslation));
              var pagewords = textAndTranslation.pagewords;
              var spellchecked = textAndTranslation.spellchecked;
              //irrelevant code follows below...
            }
          })
        .withFailureHandler(
          function(msg, element) {
            showError(msg, $('#button-bar'));
            element.disabled = false;
          })
        .withUserObject(this)
        .executeSpellChecking(origin, dest, savePrefs);
}

function executeSpellChecking(origin, dest, savePrefs) {
  console.time("function time");
  var body = DocumentApp.getActiveDocument().getBody();
  var alltext = body.getText();
  var lastchar = alltext.slice(-1);
  if (lastchar != " " && lastchar != "\n") {
    body.editAsText().insertText(alltext.length, "\n");
    alltext = body.getText();
  }

  var arr_alltext = alltext.split(/[\s\n]/);
  var pagewords = new Object;
  var pagewordsOrig = new Object;
  var pagewordsOrigOffset = new Object;
  var offset = 0;
  var curWord = "";
  var cnt = 0;
  
  for (var i = 0; i < arr_alltext.length; i++) {
    curWord = arr_alltext[i];
    if (StringHasSimeioStiksis(curWord)) {
      curWord = replaceSimeiaStiksis(curWord);
      var arr3 = curWord.split(" ");
      for (var k = 0; k < arr3.length; k++) {
        curWord = arr3[k];
        pagewords["" + (cnt+1).toString()] = curWord.replace(/[`~@#$%^&*()_|+\-="<>\{\}\[\]\\\/]/gi, '');
        pagewordsOrig["" + (cnt+1).toString()] = curWord;
        pagewordsOrigOffset["" + (cnt+1).toString()] = offset;
        offset += curWord.length;
        cnt++;
      }
      offset++;
    } else {
      pagewords["" + (cnt+1).toString()] = curWord.replace(/[`~@#$%^&*()_|+\-="<>\{\}\[\]\\\/\n]/gi, '');
      pagewordsOrig["" + (cnt+1).toString()] = curWord;
      pagewordsOrigOffset["" + (cnt+1).toString()] = offset;
      offset += curWord.length + 1;
      cnt++;
    }
  }

  var respTString = "";

  var url = 'https://www.example.org/spellchecker.php';
  var data = {
    "Text" : JSON.stringify(pagewords),
    "idOffset" : "0",
    "lexID" : "8",
    "userEmail" : "test@example.org"
  };
  var payload = JSON.stringify(data);
  var options = {
    "method" : "POST",
    "contentType" : "application/json",
    "payload" : payload
  };
  console.time("POST time");
  var response = UrlFetchApp.fetch(url, options);
  console.timeEnd("POST time");
  var resp = response.getContentText();
  respTString = resp;
  var spellchecked = JSON.parse(respTString);

  var style = {};
  for (var k in pagewords){
      if (pagewords.hasOwnProperty(k)) {
          if (spellchecked.hasOwnProperty(k)) {
            if (spellchecked[k].substr(0, 1) == "1") {
              style[DocumentApp.Attribute.FOREGROUND_COLOR] = "#000000";
            }            
            if (spellchecked[k].substr(0, 1) == "0") {
              style[DocumentApp.Attribute.FOREGROUND_COLOR] = "#FF0000";
            }
            if (spellchecked[k].substr(0, 1) == "4") {
              style[DocumentApp.Attribute.FOREGROUND_COLOR] = "#0000FF";
            }
            if (pagewordsOrigOffset[k] < alltext.length) {
              body.editAsText().setAttributes(pagewordsOrigOffset[k], pagewordsOrigOffset[k] + pagewordsOrig[k].length, style);
            }
          }
      }
  }

  console.timeEnd("function time");
  return {
    "pagewords" : pagewords,
    "pagewordsOrig" : pagewordsOrig,
    "pagewordsOrigOffset" : pagewordsOrigOffset,
    "spellchecked" : spellchecked
  }
}
M and M
  • 1
  • 1