【问题标题】:Debounce or throttle event handler in Google Apps ScriptGoogle Apps 脚本中的去抖动或限制事件处理程序
【发布时间】:2020-05-30 16:13:19
【问题描述】:

我正在寻找一种巧妙的解决方案来消除或限制 Google Sheets Apps 脚本处理程序中使用的 webhook 调用。为其他更改创建像onEdit 这样的简单触发器或“可安装触发器”是直截了当的,但两者都会为每个更改调用处理程序。如果有人正在编辑工作表并在几秒钟内更新了许多行,我只想触发一个事件而不是淹没我的 webhook 服务。 Javascript 中的常规模式是使用 setTimeoutclearTimeout 来确保事件处理程序的主体仅被调用一次,但 setTimeout 在 Google Apps 脚本运行时中不可用。

【问题讨论】:

  • 如果您打算在有用户以标准用户模式打开电子表格的情况下使用它,那么我认为您仍然可以使用 onEdit() 来收集缓冲区中的编辑,然后使用setTimeOut 从客户端侧边栏刷新该缓冲区并进行网络挂钩调用。我认为您可能希望使用缓存服务来存储短期编辑缓冲区。我在使用 Utilities.sleep() 时看到的问题如下所示,它会在超时期间完成关闭应用程序脚本,因此我总是在运行 Utllities.sleep() 之前执行 SpreadsheetApp.flush()
  • 感谢@Cooper,在读取保存在文档应用脚本上下文中的数据时,我没有想过使用侧边栏来执行“标准”javascript。在我的具体情况下,我也有来自表单提交的更新,所以我认为侧边栏不会有帮助。您如何推荐使用缓存服务?将字符串连接到单个缓存键并用锁包装?
  • 我没想过用它来执行标准的 JavaScript,除了超时功能,但我想触发服务器端功能。您可以在 CacheService 中构建缓冲区。看看 CacheService 他们有一些简单的教程来展示如何使用它。

标签: javascript google-apps-script google-sheets webhooks


【解决方案1】:

这是我的工作解决方案。它使用Utilities.sleep() 等待 10 秒,然后使用ScriptProperties 检查此事件是否是在此期间调用的最后一个事件。

如果您想解决类似问题,请分享给其他人:

/*

  Set the following project properties (File -> Project properties):

  SheetsToWatch: comma separated list of sheets to watch for events
  WebhookUrl: URL of web service to POST update to
  WebhookToken: Authorization bearer token for POST request
  SendLastValue: [optional] set if you wish last value in updated sheet to be posted


  Then create the "installable trigger" (Edit -> Current project's triggers -> Add Trigger):

  Choose which function to run: "handleChangeOrEdit"
  Select event type: "On change" or "On edit"


  A simple trigger like `onEdit` won't work have the privileges to call `UrlFetchApp.fetch`.

 */


function handleChangeOrEdit(event) {
  var sheetId = event.source.getId();
  var sheetUrl = event.source.getUrl();
  var sheetName = (event.range ? event.range.getSheet() : SpreadsheetApp.getActiveSheet()).getName();

  // Trigger only on those sheets we're configured to watch (or all if not specified)
  var sheetsToWatch = PropertiesService.getScriptProperties().getProperty("SheetsToWatch");
  if (sheetsToWatch && sheetsToWatch.split(",").indexOf(sheetName) == -1) {
    return;
  }

  var eventId = Utilities.getUuid();
  setEventTriggerWinner(eventId);
  // OPTIONAL: You might want to save values from each edit here, to be dealt with by the "winner"
  Utilities.sleep(10000);   // Wait to see if another Change/Edit event is triggered
  if (getEventTriggerWinner() == eventId) {
    Logger.log(`Trigger Winner: ${eventId}`);
    callWebhook({eventId, sheetId, sheetUrl, sheetName});
  }
}

function setEventTriggerWinner(value) {
  // Wrapping setProperty in a Lock probably isn't necessary since a set should be atomic
  // but just in case...
  var lock = LockService.getScriptLock();
  if (lock.tryLock(5000)) {
    PropertiesService.getScriptProperties().setProperty("eventTriggerWinner", value);
    lock.releaseLock();
  }
}

function getEventTriggerWinner() {
  return PropertiesService.getScriptProperties().getProperty("eventTriggerWinner");
}

function callWebhook(data) {
  var url = PropertiesService.getScriptProperties().getProperty("WebhookUrl");
  var token = PropertiesService.getScriptProperties().getProperty("WebhookToken");
  UrlFetchApp.fetch(url, {
    headers: {"Authorization": `Bearer ${token}`},
    method: "post",
    contentType: "application/json",
    payload: JSON.stringify(data)
  });
}

【讨论】:

    【解决方案2】:

    如果你需要setTimeoutclearTimeout,在你的代码中包含这个sn-p:

    https://script.google.com/d/1m7GaM7Z7ivgHaAwXcgpsAwg-e7tMDm9GsN5P-xpcAGT_P6cleF_mKGD3/edit?usp=drive_web

    【讨论】:

      猜你喜欢
      • 2021-05-01
      • 2021-10-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-04-08
      • 1970-01-01
      • 2015-03-17
      相关资源
      最近更新 更多