11

在这个(我承认不是很多......)有趣的问题背后是一个关于我使用的解决方法的真正问题,但我并没有真正理解它是如何工作的。

首先简要描述一下我的用例,所有这些都发生在侧边栏中显示的文档绑定 UiApp 中:

我必须在用 GAS 编写的邮件合并应用程序中创建并通过电子邮件发送数百个文档。在不达到 5 分钟执行时间限制的情况下,一批处理当然需要很长时间,所以我尝试了几种不同的解决方法来完成任务:

  1. 当我开始进程时使用时间戳(存储在 ScriptProperties 中),当我达到接近限制的预定义值时,我存储当前值(指针、有用的变量......)并返回到用户界面,要求用户继续(或不)。这工作得很好,但需要一个人的行动来完成整个任务。
  2. 因此,我使用在第一个处理程序调用中创建的计时器触发器设置了一个解决方案,该触发器调用了文档创建/发送函数。这也很好用,但调用函数的触发器无法与 UI 交互,因为似乎只有处理函数可以更新 UI。问题是我无法显示进度,也无法轻松显示进程何时结束。
  3. 然后我想起了一个前段时间写的一个小应用程序,只是为了好玩:它是一个使用复选框作为服务器处理程序触发器的计时器(来自 Romain Vialard 很久以前在旧谷歌论坛上提出的一个想法)并决定试试这个我的邮件发送过程中的技巧。

它工作得很好,我处理 40 个文档批次是每个调用(大约 3 分钟)然后暂停一段时间,然后重新开始,直到它完成。每个调用都由 checkBox 链接服务器处理程序触发,复选框本身在处理程序函数中发生更改,以这种方式创建自己的触发器。

我的问题(最后;-)是:知道整个过程可能需要 30 到 60 分钟,这有多精确? 这些服务器处理程序函数如何/为什么被视为多个进程,因为它们是从函数本身内部创建的?

我希望我足够清楚,(我对此表示怀疑,因为这在我看来有点混乱:-)

我加入了给我这个想法的时钟测试应用程序的代码,它可能会让事情更容易理解。

function doGet() {
  var app = UiApp.createApplication().setTitle('Counter/Timer');
  var Panel = app.createAbsolutePanel().setStyleAttribute('padding','35');
  var counter = app.createHTML().setId('counter').setHTML('<B>Timer = wait</B>').setStyleAttribute('fontSize','40px');// set start display
  var clo = app.createTextBox().setName('clo').setId('clo').setValue('0').setVisible(false);//set start value in seconds
  var handler1 = app.createServerHandler('doSomething').addCallbackElement(Panel);
  var chk1 = app.createCheckBox('test1').addValueChangeHandler(handler1).setVisible(true).setId('chk1').setVisible(false);
  app.add(Panel.add(chk1).add(counter).add(clo));
  chk1.setValue(true,true);// start the process
  return app}

function doSomething(e) {
  var app = UiApp.getActiveApplication();
  var xx = Number(e.parameter.clo);
  var disp = app.getElementById('counter')
  xx++ ;// replace by xx-- to count downwards
  if(xx>600){ // 10 minutes timeout for example
  disp.setHTML('<B> GAME OVER ;-)</B>').setStyleAttribute('fontSize','80px').setStyleAttribute('color','RED')
  return app
  }
  var cnt = app.getElementById('clo').setValue(xx)
  disp.setHTML('<B>'+T(xx)+'</B>')
  Utilities.sleep(1000); // instead of sleeping do something !
// below comes the "active" part
  var chk1 = app.getElementById('chk1').setValue(false,false)
  var chk1 = app.getElementById('chk1').setValue(true,true)
  return app;
}

function T(val){
  var min = parseInt(val/60);
  var sec = val-(60*min);
  if(sec<10){sec='0'+sec}
  if(min<10){min='0'+min}
  var st = '>  '+min+':'+sec
  return st
}

在此处输入图像描述

4

1 回答 1

10

服务器处理程序函数调用不是独立进程,因为它们“是从函数本身内部创建的”的说法并不完全正确。

chk1您已经使用服务器处理程序设置了一个复选框元素doSomething。每当检查复选框时,就会将事件派往服务器。(...并且您的脚本在每次chk1.setValue()调用时都会导致这些事件)checkBox您的浏览器中正在运行和周围的 UI 代码 - 单击“显示源代码”或使用资源管理器查看谷歌服务器为您的浏览器提供的内容。(警告 - 它被混淆了。但您可能会识别出一些字符串,并从中识别出您的客户端代码。)

这是Class ServerHandler的文档中告诉我们的:

当调用 ServerHandler 时,它所引用的函数会在 Apps Script 服务器上以“新”脚本的形式调用。

这是延长您的操作时间的关键:每个分派的事件都会导致在doSomething()一个全新的操作上下文中调用 - 就像您在不同的浏览器中打开了脚本编辑器,并在您的脚本上单击了“运行”。“新鲜”脚本无法访问先前运行的 var 值......但它也有自己的一组操作限制,包括计时器。

PS:您应该使用 Lock 确保服务器端处理程序是“线程安全的”,因为您正在访问可能被多个doSomething()回调实例访问的共享资源。与此相关的是,使用此脚本可能会达到另一个限制:

屏幕截图 - 错误

只是为了好玩,我评论了.setVisible(false)chk1所以复选框是可见的。然后我迅速点击了几十次。时间显示乱码,最终弹出上述错误。(几分钟后)当然,这是一种人为的情况,但仍然是一种很容易避免的错误状态。

PPS:我想知道是否可以使用相同的技术来调度多个并行的服务器端处理程序,从而减少完成整个工作所用的时间?

于 2013-06-03T00:53:58.413 回答