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

时间:2013-06-02 19:38:15

标签: google-apps-script

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

首先简要介绍一下我的用例,所有这些都发生在一个文档中,UiApp出现在侧边栏中:

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

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

    enter image description here

1 个答案:

答案 0 :(得分:10)

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

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

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

  

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

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

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

Screenshot - error

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

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