Google Apps 脚本中的去抖或节流事件处理程序

Pau*_*gan 2 javascript webhooks google-sheets google-apps-script

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

Pau*_*gan 6

这是我的工作解决方案。它用于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)
  });
}

Run Code Online (Sandbox Code Playgroud)