简体   繁体   English

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

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

Behind this (not so much I admit...) funny question is a real question about a workaround I use without really understanding how it works. 在这背后(不是我承认......)有趣的问题是关于我使用的解决方法的真正问题,而没有真正理解它是如何工作的。

First a brief description of my use case, all this is happening in a document bound UiApp showing up in the sidebar : 首先简要介绍一下我的用例,这一切都发生在侧边栏中显示的文档绑定UiApp中:

I have to create and send by email a couple of hundred documents in a mail merge application written in GAS. 我必须在用GAS编写的邮件合并应用程序中通过电子邮件创建和发送几百个文档。 It takes of course far too long to be processed in one batch without hitting the 5 minutes execution time limit so I tried a few different workarounds to get the task done : 当然,在没有达到5分钟执行时间限制的情况下,在一个批处理中花费太长时间,所以我尝试了几个不同的解决方法来完成任务:

  1. use a time stamp (stored in ScriptProperties) when I start the process and when I reach a predefined value near the limit I store the current values (pointers, useful vars...) and return to the user interface asking the user to continue (or not). 当我启动进程时使用时间戳(存储在ScriptProperties中),当我达到接近极限的预定义值时,我存储当前值(指针,有用的变量......)并返回用户界面,要求用户继续(或不)。 That works pretty well but need a human action to complete the whole task. 这很有效但需要人工操作来完成整个任务。
  2. So I setup a solution using a timer trigger that I create in the first handler call and this trigger calls the doc creation / sending function. 所以我使用我在第一个处理程序调用中创建的计时器触发器来设置解决方案,此触发器调用doc创建/发送功能。 This is also working nicely but the trigger called function are not able to interact with the UI since it seems that only handler functions can update the UI. 这也很好用,但是被称为函数的触发器无法与UI交互,因为似乎只有处理函数可以更新UI。 The problem there is that I cannot show the progress nor easily show when the process ends. 问题在于,我无法显示进度,也无法在流程结束时轻松显示。
  3. Then I remembered a small app that a wrote some time ago just for fun : it was a timer that used a checkBox as a server handler trigger (from an idea suggested long ago by Romain Vialard on the old Google forum) and decided to try this trick in my mail sending process. 然后我想起了一段时间前写的一个小应用程序只是为了好玩:它是一个使用checkBox作为服务器处理程序触发器的计时器(来自很久以前Romain Vialard在旧Google论坛上提出的想法)并决定尝试这个在我的邮件发送过程中的技巧。

It works perfectly, I process 40 documents batches is each call (during about 3 minutes) then pause for a while and start again until it gets finished. 它工作得很好,我处理40个文件批次是每次调用(大约3分钟)然后暂停一段时间并重新开始直到它完成。 Each call is triggered by the checkBox linked server handler, the check box itself is changed in the handler function, creating its own trigger this way. 每个调用都由checkBox链接服务器处理程序触发,复选框本身在处理程序函数中更改,以这种方式创建自己的触发器。

My question (finally ;-) is : knowing that the whole process might take 30 to 60 minutes how precisely is that possible ? 我的问题(最后;-)是:知道整个过程可能需要30到60分钟,这可能有多精确? How / why are these server handler function considered as multiple process since they are created from inside the function itself ? 这些服务器处理函数如何/为什么被视为多个进程,因为它们是从函数本身内部创建的?

I hope I'm clear enough, (which I doubt since it is a bit confuse in my mind :-) 我希望我足够清楚,(我怀疑,因为它在我的脑海里有点混乱:-)

I join below the code of the clock test app that gave me the idea, it will probably make things easier to understand. 我加入了时钟测试应用程序的代码,它给了我这个想法,它可能会让事情更容易理解。

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
}

在此输入图像描述

The statement that the server handler function calls aren't independent processes because they "are created from inside the function itself" isn't quite true. 服务器处理程序函数调用的语句不是独立进程,因为它们“是从函数本身内部创建的”并不完全正确。

You've set up a checkBox element chk1 with a server handler doSomething . 您已经使用服务器处理程序doSomething设置了一个checkBox元素chk1 Whenever the checkBox is checked, then, an event is dispatched to the server. 每当选中checkBox时,都会将一个事件分派给服务器。 (...and your script is causing those events with every chk1.setValue() call) The checkBox and surrounding UI code is running in your browser - click "show source" or use an explorer to see what's been served to your browser by the google servers. (...并且您的脚本在每次chk1.setValue()调用时都会导致这些事件) checkBox和周围的UI代码在您的浏览器中运行 - 单击“显示源”或使用资源管理器查看chk1.setValue()浏览器提供的内容谷歌服务器。 (Warning - it's obfuscated. But you might recognize some of your strings, and from that your client-side code.) (警告 - 它被混淆了。但你可能会认识到你的一些字符串,以及你的客户端代码。)

Here's what we're told in the documentation for Class ServerHandler : 以下是我们在Class ServerHandler的文档中所说的内容

When a ServerHandler is invoked, the function it refers to is called on the Apps Script server in a "fresh" script. 当调用ServerHandler时,它引用的函数在Apps脚本服务器上以“新鲜”脚本调用。

That's the key to extending your operating time: each dispatched event results in invocation of doSomething() in a completely new operating context - it's like you've opened the script editor in a different browser, and clicked "run" on your script. 这是延长操作时间的关键:每个调度事件都会导致在全新的操作上下文中调用doSomething() - 就像您在其他浏览器中打开脚本编辑器一样,并在脚本上单击“运行”。 The "fresh" script has no access to var values of previous run... but it is also given it's own set of operating restrictions, including timers. “新鲜”脚本无法访问以前运行的var值...但它也有自己的一组操作限制,包括计时器。

PS: You should make sure that server-side handler is "thread safe" by using Lock, since you're accessing shared resources that may be accessed by multiple instances of the doSomething() callback. PS:您应该通过使用Lock确保服务器端处理程序是“线程安全的”,因为您正在访问可由多个doSomething()回调实例访问的共享资源。 Related to that, it's possible to hit another limit with this script: 与此相关,可以使用此脚本达到另一个限制:

截图 - 错误

Just for fun, I commented out .setVisible(false) on chk1 , so the checkBox would be visible. 为了好玩,我在chk1上注释了.setVisible(false) ,因此checkBox是可见的。 I then rapidly clicked it several dozen times. 然后我快速点击了几十次。 The time display ran out-of-order, and eventually the above error popped up. 时间显示无序运行,最终出现上述错误。 (minutes later) It's an artificial situation, of course, but still an error state that's easily avoided. (几分钟后)当然,这是一个人为的情况,但仍然是一个容易避免的错误状态。

PPS: I wonder if one could use the same technique to dispatch multiple parallel server-side handlers, and thus reduce the elapsed time to complete the whole job? PPS:我想知道是否可以使用相同的技术来分派多个并行服务器端处理程序,从而减少完成整个作业所用的时间?

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

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