如何使用 FormApp...getResponses() 减少脚本执行时间?

How to reduce time of script execution with FormApp...getResponses()?

表单内置的Email收集功能为Text格式。我们有很多员工填写这个表格,所以从下拉列表中选择比手动输入每个员工的电子邮件更方便。内置的邮件收集功能不允许使用下拉列表问题类型,所以我不得不禁用它并使用脚本将响应发送到电子邮件。

此外,当您select表格中的选项之一(传输)时,您需要插入一个额外的带有数据的更正行。这也是脚本完成的。

脚本运行良好,但不幸的是,它的执行时间达到了 45 秒。我认为这是异常大的。

更新代码:

var FORM_ID = '#####';
var SHEET_NAME = 'Operations';

function sendFormToEmail() {
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  console.time('section10'); // 25579ms
  var sheet = ss.getSheetByName(SHEET_NAME);
  console.timeEnd('section10');
  
  // Open a form by ID and log the responses to each question.
  var form = FormApp.openById(FORM_ID);
  
  var formResponses = form.getResponses();
  var i = formResponses.length - 1;
  var formResponse = formResponses[i]; // Last item
  var itemResponses = formResponse.getItemResponses();
  
  var length = itemResponses.length;
  Logger.log("length = " + length);

  var emailTo = itemResponses[0].getResponse();  // Returns the email if given
  var cp = itemResponses[1].getResponse();  // Counterparty
  var subject = "Input form: "+ cp;
  var datePay = itemResponses[2].getResponse();  // Date of the operation
  var dateAccept = itemResponses[3].getResponse();  // Date of acceptance
  var sum = itemResponses[4].getResponse() ;  // Amount
  var what = itemResponses[5].getResponse();  // Operation type

  var comm =  "Correction " + itemResponses[length - 1].getResponse(); // Last response
  var sum_1 = parseFloat(sum.replace(/,/, '.')) * (-1);  // Amount * (-1)
  var timestamp = new Date();
  var textBody = "Operation: " + timestamp + ";\n";

  for (var j = 0; j < itemResponses.length; j++) {
    var itemResponse = itemResponses[j];
    var resp = itemResponse.getResponse();
    textBody += itemResponse.getItem().getTitle() + "=" + resp + ";\n";
  }

  if (what == 'Transfer') { // If the transfer between your accounts
    var lr = sheet.getLastRow();
    sheet.insertRowBefore(lr);
    var values = [datePay, dateAccept, "", sum_1, "", "", "", "", comm, "", "", "", what, timestamp, "", "", ""];
    Logger.log(values);
    console.time('section12'); // 19449ms
    sheet.getRange(lr, 2, 1, 17).setValues([values]);
    console.timeEnd('section12')
  }
  
  if(emailTo !== undefined){
    GmailApp.sendEmail(emailTo, subject, textBody);
  }
}

一开始以为是验收的大尺寸sheet(约13000行)。我创建了一份table,减少了几十行,但是速度并没有增加多少。 然后我从表单中删除了答案(只有不到 9000 个)- 同样的事情,我没有获得太多性能提升。

有人知道如何更改脚本算法以提高其性能吗?

===

结论

感谢@TheMaster 对 console.time() 的帮助。多亏了这个工具,我发现了代码中的瓶颈。更准确地说,代码运行良好,但它与代码交互的 spreadsheets 系统的结构有关。结构有待优化。 还有一个想法可能是针对 Google 开发人员的。如果有一个工具可以直观地(图形化地)显示构成单个系统的 spreadsheet 和 sheet 之间的关系,那就太好了。也许具有某种反映其块之间交互时间的数字特征。这样可以快速消除系统中的此类瓶颈并对其进行改进。

调查:

  • 使用每个代码部分的 console.time()console.timeEnd() 并计算到达每个代码部分所花费的时间来确定问题。

问题:

中所述,这是由于膨胀的传播 sheet 和许多 sheet 和导入公式所致。这导致需要更多时间来获得准确的 sheet 并使用 setValues:

console.time('section10'); // 25579ms 
var sheet = ss.getSheetByName(SHEET_NAME); 
console.timeEnd('section10');
//....
console.time('section12'); // 19449ms 
sheet.getRange(lr, 2, 1, 17).setValues([values]);
console.timeEnd('section12')

可能的解决方案:

  • 减少 sheet 的数量
  • 减少相互关联的传播sheets (=import* 公式)
  • 删除底部的空行和每个右侧的空列sheet。