公式使 SpreadsheetApp 在 Google Sheets 脚本中运行速度变慢

Max*_*rov 4 performance triggers google-sheets google-apps-script google-sheets-formula

这个问题最近出现~2022年8月25日。

\n

我的onEdit触发器依赖于工作表名称,并且脚本运行时间太长而无法获取它。这是在我的环境中重现错误的最小脚本:

\n
function onEdit(e) {\n  var t = new Date();\n  console.log(\'we are in onEdit!\');\n  var sheetName = e.range.getSheet().getName();\n  console.log(sheetName);\n  console.log(\'We got sheet name. Time past = \' + (new Date() - t));\n}\n
Run Code Online (Sandbox Code Playgroud)\n

该函数range.getName()运行速度太慢,并且脚本超时。

\n

我以前从未见过这种行为。其他样本:SpreadsheetApp. 在我的另一个文件中,这一行需要 30 秒:

\n

SpreadsheetApp.openById(id);

\n

重现错误

\n

这是重现错误所需的最少代码的文件:

\n

onEdit 崩溃测试 \xe2\x9e\xa1\xef\xb8\x8f e.range.getSheet().getName()

\n

请复印一份。

\n

重现onEdit错误

\n
    \n
  1. 在第一个单元格中打印 1
  2. \n
  3. 立即按 Enter 键并打印 2
  4. \n
  5. 重复几次
  6. \n
  7. 转到脚本编辑器并查看执行时间
  8. \n
\n

原来的

\n

它有一些公式,并且脚本运行速度很快。

\n

我的目标是理解为什么公式会导致sheet.getName()方法变慢。

\n

错误

\n
\n

超过最大执行时间

\n
\n

最大。onEdit 的时间为 30 秒。测试表明它的sheet.getName()工作时间太长了。如果没有该功能,我无法看到编辑后的工作表名称getName(),因为它位于触发器内。

\n

我的修复

\n

即使是 Lock 和 Cache 也没有解决这个问题:

\n

注射

\n
  /**  Injection. Use Cache to Speed Up `getName()` method */ \n  var lock = LockService.getScriptLock();\n  try {\n    lock.waitLock(30000); // wait 30 seconds for others\'\n  } catch (e) {\n    throw \'Could not get Sheet Name in 30 sec. :(\';\n  } \n  var sheetNameCacheKey = \'sheetNameforThoseWhoForgotten\';\n  var c = CacheService.getUserCache();\n  var sheetNameFromCache = c.get(sheetNameCacheKey);\n  if (sheetNameFromCache) {\n    sets.sheetName = sheetNameFromCache;  \n    console.log(\'Prolongated Cache Life!\');\n    c.put(sheetNameCacheKey, sets.sheetName, 5);\n  } else {\n    // This function is actually needed, \n    // but does not work fast\n    //               \xe2\x8f\xb3\n    sets.sheetName = sets.sheet.getName(); \n    // Write Sheet Name to Memory for 5 seconds\n    c.put(sheetNameCacheKey, sets.sheetName, 5);\n    console.log(\'Added Sheet Name to Cache!\');\n  }\n  lock.releaseLock();\n  /**  End of Injection */\n
Run Code Online (Sandbox Code Playgroud)\n

这段代码使我的触发器更加稳定,但仍然偶尔会失败。

\n

速度测试

\n
\n

读取工作表名称的时间为:151766

\n
\n

这已经快3分钟了!但这个数字会发生变化,有时可能需要大约 150 毫秒才能运行。

\n

笔记

\n

我的猜测是这个错误是由 Google Sheets 的最新大更新和新功能引起的:

\n\n

IMPORTRANGE我这么认为是因为这个问题与错误有关:

\n
\n

导入范围内部错误。

\n
\n

Tan*_*ike 6

问题和解决方法:

\n

我也经历过和你一样的情况。似乎当电子表格中包含公式的大量计算时,就会发生这种情况。在这种情况下,这也体现在脚本的触发和直接执行上。我不确定这种情况是当前规范还是错误。

\n

当时我注意到,在这种情况下,Google Apps Script 的方法的处理成本存在差异。例如,当我在示例电子表格中看到您提供的脚本时,我发现了以下脚本。

\n
function onEdit(e) {\n  var t = new Date();\n  console.log(\'we are in onEdit!\');\n  var sheetName = e.range.getSheet().getName(); // times out \xe2\x8f\xb3!\n  console.log(sheetName);\n  console.log(\'We got sheet name. Time past = \' + (new Date() - t));\n}\n
Run Code Online (Sandbox Code Playgroud)\n

当以该脚本为样本时,在这种情况下, 的流程成本e.range.getSheet()远低于e.range.getSheet().getName(). 由此,作为示例,为了检索活动工作表的工作表名称,需要使用低处理成本的方法。在此解决方法中,作为示例情况,使用您提供的电子表格,我想从您的上述脚本中提出一个修改后的脚本。

\n

在此解决方法中,我使用e.range.getSheet().getSheetId()而不是e.range.getSheet().getName(). 并且,使用Sheets API。因为 的处理成本e.range.getSheet().getSheetId()低于e.range.getSheet().getName(),并且,为了从工作表 ID 检索工作表名称,当使用电子表格服务(SpreadsheetApp)时,处理成本会变高。

\n

当此解决方法反映在您的上述脚本中时,它会变成如下所示。

\n

示例脚本:

\n

请将以下脚本复制并粘贴到您提供的电子表格的脚本编辑器中。并且,请启用 Sheets API。并且,请将 OnEdit 触发器安装到 的函数中installedOnEdit。当您使用此脚本时,请在单元格中输入一个值。这样,脚本就可以工作,您可以在日志中看到活动工作表的工作表名称。

\n
function installedOnEdit(e) {\n  var t = new Date();\n  console.log(\'we are in onEdit!\');\n  var sheetId = e.range.getSheet().getSheetId();\n  var ssId = e.source.getId();\n  const sheetName = Sheets.Spreadsheets.get(ssId).sheets.find(s => s.properties.sheetId == sheetId).properties.title;\n  console.log(sheetName)\n  console.log(\'We got sheet name. Time past = \' + (new Date() - t));\n}\n
Run Code Online (Sandbox Code Playgroud)\n
    \n
  • 当将值放入单元格时,您可以在日志中看到工作表名称。在我的测试中,处理时间约为 3 秒。

    \n
  • \n
  • 就我而言,作为这样的解决方法,当在包含公式的大量计算的电子表格中使用 OnEdit 触发器和脚本时,我研究低成本方法并使用它们。

    \n
  • \n
\n

笔记:

\n
    \n
  • 在此解决方法中,当执行 OnEdit 触发器函数时,将继续计算公式。所以,请小心这一点。
  • \n
\n

附加信息:

\n

作为补充信息,当我测试这种情况时,发现单元格值没有被检索,并且该过程不受公式计算的影响。当看到上面的示例脚本时,不会检索单元格值。看来这样可以减少公式计算时的过程成本。

\n

另一方面,当使用Sheets API将单元格值放入单元格时,发现该过程不受公式计算的影响。此外,我们还发现,当使用电子表格服务(SpreadsheetApp)输入单元格值时,该过程会受到公式计算的影响。

\n