繁体   English   中英

当我在GAS中“睡觉”时会发生什么? (执行时间限制解决方法)

[英]What happens when I “sleep” in GAS ? (execution time limit workaround)

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

首先简要介绍一下我的用例,这一切都发生在侧边栏中显示的文档绑定UiApp中:

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

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

它工作得很好,我处理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
}

在此输入图像描述

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

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

以下是我们在Class ServerHandler的文档中所说的内容

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

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

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

截图 - 错误

为了好玩,我在chk1上注释了.setVisible(false) ,因此checkBox是可见的。 然后我快速点击了几十次。 时间显示无序运行,最终出现上述错误。 (几分钟后)当然,这是一个人为的情况,但仍然是一个容易避免的错误状态。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM