【问题标题】:Event duplication?事件重复?
【发布时间】:2013-06-09 07:02:33
【问题描述】:

我在 GAS 上遇到了一个关于 Google 电子表格脚本的奇怪问题。

有时,由下面的“mouse up”事件触发的函数会执行两到三次,每次运行之间相差几秒。并非每次都会发生,问题看起来非常随机。

按钮:

var button = app.createButton("Alocar").addClickHandler(app.createClientHandler().forEventSource().setEnabled(false)).addMouseUpHandler(app.createServerHandler("onAllocateTask").addCallbackElement(panel));

“onAllocateTask”开头:

function onAllocateTaskSheet(e) {
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  ss.toast("Aguarde alguns minutos.", "Atenção", 8);

  var task = e.parameter[TASK_ALLOCATION_PARAMETER];
  if (task == null || task == "") {
    return;
  }

  ... continues ...
}

我可以保证函数“onAllocateTaskSheet”在代码中仅被调用一次(在上面的一段代码中)。另一个重要信息是此过程 (onAllocateTaskSheet) 的执行时间很长(它会复制数十个 Google Docs 文档)。

是否有重复执行此功能的解释?也许我使用了错误的事件?某些内部错误会导致程序重新执行?

再次感谢您的帮助!

编辑:

好的,现在我可以确认问题与按钮或事件 mouseUp 无关。问题是函数“onAllocateTask”在 X 分钟后被重新执行。当 onAllocateTask 快速完成时(例如不到 1 分钟),它不会发生。我猜这与一些超时或内部错误有关。

Unexpected exception upon serializing continuation Google Apps Script 在这里,有人说 Google Apps 脚本丢失了“循环指针”。有可能吗?

【问题讨论】:

  • 正如我在回答中所说,我没有明确的解释,但我也注意到,当函数执行时间过长时会发生这种重新执行......锁定“技巧”解决了这个问题完全适合我。你试过了吗?
  • @Sergeinsas 我昨天试过了,问题解决了。谢谢。
  • 很高兴 :-) 我在每次漫长而缓慢的操作中都使用它,并且每次都能解决问题。

标签: google-apps-script


【解决方案1】:

我对此没有任何好的解释,但它也发生在我身上。最好的解决方案是使用锁定服务,它可以保证一键只执行一个函数的一个实例。 很容易实现:

在你的函数开始时获得这样的锁:

  ...
  var lock = LockService.getPublicLock();
  var success = lock.tryLock(5000);
   if (!success) {
     Logger.log('tryLock failed to get the lock');
     return
   }

并在正常执行结束时像这样释放锁:

   lock.releaseLock();

(拍摄from documentation here

【讨论】:

  • 这样的 hack 会导致非常奇怪的行为。您至少应该使用 PrivateLock 来减轻副作用。悬停这并不能解决问题,因为代码重新执行只是被推迟了。
  • 我确实假设在第一次单击 clientHandler 时按钮被禁用,因为这就是我使用的。尽管如此,仍然会发生服务器功能尝试在没有任何点击的情况下第二次执行并且锁定解决方案拒绝...它不会推迟。你在想什么奇怪的行为?
  • 获取锁不会阻止服务器功能在不单击的情况下第二次运行 - 您有时必须释放锁,甚至私有锁也是脚本全局的。如果您在被禁用时真的有按钮触发事件 - 我真的不相信您可以使用客户端处理程序在隐藏字段中设置标志以防止执行:
  • 我不确定问题是否是按钮在禁用时触发事件(实际上在我看来这将是一个错误,即使我使用不同的事件,如 mouseUp 和 click)。无论如何我会尝试锁定解决方案,谢谢。
  • var hidden=app.createHidden('flag','go'); var clienthandler = app.createClientHandler().forEventSource().setEnabled(false).forTargets(hidden).setValue('stop') 在你刚刚测试的处理程序中'if (e.parameter.flag = 'go')`
【解决方案2】:

只需在服务器请求运行时禁用客户端上的按钮即可。但是您绝对应该对服务器和客户端处理程序使用相同的事件

var button = app.createButton("Alocar");
button.addClickHandler(app.createClientHandler().forEventSource().setEnabled(false));
button.addClickHandler(app.createServerHandler("onAllocateTask").addCallbackElement(panel));

【讨论】:

  • 感谢您的回答,我会尝试使用相同的事件(目前我使用 onMouseUp 触发该功能并使用 onClick 禁用按钮),虽然我认为这不是问题.有时第二次执行在 1 分钟后开始,按钮已经禁用:/
猜你喜欢
  • 1970-01-01
  • 2020-07-21
  • 2016-04-24
  • 1970-01-01
  • 2021-11-03
  • 1970-01-01
  • 2019-08-17
  • 2010-11-10
  • 1970-01-01
相关资源
最近更新 更多