当我在GAS中“睡眠”时会发生什么? (执行时间限制解决方法)
在这个背后(不是我承认…)有趣的问题是一个真正的问题关于我使用的解决方法,而不真正理解它是如何工作的。
首先简要介绍一下我的用例,所有这一切都发生在一个绑定UiApp的文档中,
我必须创build并通过电子邮件发送几百个文件在GAS编写的邮件合并应用程序。 如果没有达到5分钟的执行时间限制,在一个批处理过程中需要很长的时间,所以我尝试了几个不同的解决方法来完成任务:
- 使用时间戳(存储在ScriptProperties中)当我开始进程,当我达到预定义的值接近极限时,我存储当前值(指针,有用的variables…),并返回到用户界面,要求用户继续或不)。 这工作相当好,但需要人为的行动来完成整个任务。
- 因此,我使用在第一个处理程序调用中创build的定时器触发器来设置解决scheme,并且此触发器调用文档创build/发送function。 这也很好地工作,但称为函数的触发器不能够与UI进行交互,因为似乎只有处理函数可以更新UI。 问题是我不能显示进程,也不能在进程结束时显示。
- 然后,我想起了一个小应用程序,前段时间为了好玩而写了一个小程序:它是一个使用CheckBox作为服务器处理程序触发器的计时器(从旧的Google论坛上的Romain Vialard提出的想法),并决定尝试诡计在我的邮件发送过程中。
它完美的工作,我处理40个文件批次是每个电话(约3分钟),然后暂停一会儿,再次开始,直到它完成。 每个调用都由checkBox链接的服务器处理程序触发,checkbox本身在处理函数中被更改,以这种方式创build自己的触发器。
我的问题(最后;-)是:知道整个过程可能需要30到60分钟,究竟有多精确? 如何/为什么这些服务器处理程序函数被视为多个进程,因为它们是从函数本身内部创build的?
我希望我很清楚,(我怀疑,因为它在我心中有点混乱:-)
我在下面的时钟testing应用程序的代码给了我这个想法,它可能会让事情变得更容易理解。
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 }
服务器处理程序函数调用的声明不是独立的进程,因为它们是“从函数本身内部创build的”并不完全正确。
你已经用服务器处理程序doSomething
设置了一个checkBox元素chk1
。 每当checkBox被选中,一个事件被分派到服务器。 (…并且脚本正在使用每个chk1.setValue()
调用导致这些事件) checkBox
和周围的UI代码正在您的浏览器中运行 – 单击“show source”或使用资源pipe理器查看浏览器提供的内容谷歌服务器。 (警告 – 它被混淆了,但是你可能会识别你的一些string,并从你的客户端代码中识别出来)。
这是我们在Class ServerHandler的文档中告诉的:
当一个ServerHandler被调用时,它所引用的函数在Apps脚本服务器上以“新鲜”脚本被调用。
这是延长操作时间的关键:每个调度的事件都会导致在全新的操作环境中调用doSomething()
,就像您在不同的浏览器中打开了脚本编辑器,并在脚本上单击“运行”。 “新鲜的”脚本不能访问以前运行的var值,但是它也有自己的一套操作限制,包括定时器。
PS:你应该确保服务器端的处理程序是使用Lock进行“线程安全”的,因为你正在访问doSomething()
callback的多个实例可以访问的共享资源。 与此相关的是,可以用这个脚本达到另一个限制:
为了好玩,我在chk1
上注释了.setVisible(false)
,所以checkBox就会显示出来。 然后我迅速点击了几十次。 时间显示无序,最终popup上述错误。 (几分钟后)这当然是人为的情况,但仍然是一个容易避免的错误状态。
PPS:我想知道是否可以使用相同的技术来分派多个并行的服务器端处理程序,从而减less完成整个工作所花费的时间?