Что происходит, когда я "сплю" в ГАЗ? (временное ограничение выполнения)

За этим (не столько я признаю...) забавный вопрос - это реальный вопрос об обходном пути, который я использую, даже не понимая, как это работает.

Сначала краткое описание моего варианта использования, все это происходит в документе, связанном с UiApp, отображаемом на боковой панели:

Мне нужно создать и отправить по электронной почте пару сотен документов в приложение для слияния, написанное в GAS. Требуется, конечно, слишком долго, чтобы обрабатываться в одной партии, не достигнув предела времени выполнения 5 минут, поэтому я попробовал несколько разных обходных решений, чтобы выполнить задачу:

  • используйте временную метку (хранящуюся в ScriptProperties), когда я запускаю процесс, и когда я достигаю предопределенного значения рядом с пределом, я сохраняю текущие значения (указатели, полезные vars...) и возвращаются в пользовательский интерфейс, запрашивая у пользователя продолжить (или нет). Это работает очень хорошо, но для выполнения всей задачи требуется человеческое действие.
  • Поэтому я настраиваю решение, используя триггер таймера, который я создаю в первом вызове обработчика, и этот триггер вызывает функцию создания/отправки документа. Это также хорошо работает, но функция-триггер не может взаимодействовать с пользовательским интерфейсом, поскольку кажется, что только функции обработчика могут обновлять пользовательский интерфейс. Проблема в том, что я не могу показать прогресс и не показать, когда процесс завершится.
  • Затем я вспомнил небольшое приложение, которое написал некоторое время назад просто для удовольствия: это был таймер, который использовал 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
}

enter image description here

Ответ 1

Утверждение, что вызовы функций обработчика сервера не являются независимыми процессами, потому что они "созданы из самой функции", не совсем верно.

Вы установили элемент checkbox chk1 с обработчиком сервера doSomething. Всякий раз, когда checkBox проверяется, то событие отправлено на сервер. (... и ваш script вызывает эти события при каждом вызове chk1.setValue()). checkBox и окружающий код пользовательского интерфейса запущены в вашем браузере - нажмите "показать источник" или используйте проводник, чтобы узнать, что было подано вашим браузером на серверах google. (Предупреждение - оно запутано, но вы можете распознать некоторые из ваших строк и из этого кода вашей клиентской части.)

Вот что нам сказано в документации для Class ServerHandler:

При вызове ServerHandler функция, на которую она ссылается, вызывается на сервере Apps script в "свежий" script.

Это ключ к увеличению времени работы: каждое отправленное событие приводит к вызову doSomething() в совершенно новом рабочем контексте - это похоже на то, что вы открыли редактор script в другом браузере и нажали кнопку "запустить", на вашем script. "Свежий" script не имеет доступа к значениям var предыдущего запуска... но ему также предоставляется собственный набор рабочих ограничений, включая таймеры.

PS. Вы должны убедиться, что обработчик на стороне сервера является "потокобезопасным" с помощью блокировки, поскольку вы обращаетесь к общим ресурсам, к которым могут обращаться несколько экземпляров обратного вызова doSomething(). В связи с этим, с этим script можно нанести еще один предел:

Screenshot - error

Просто для удовольствия, я прокомментировал .setVisible(false) на chk1, поэтому checkBox будет виден. Затем я быстро щелкнул его несколько десятков раз. Отображение времени закончилось не по порядку, и в итоге вышла ошибка выше. (через несколько минут) Это, конечно, искусственная ситуация, но все же ошибка, которую легко избежать.

PPS: Интересно, можно ли использовать один и тот же метод для отправки нескольких параллельных обработчиков на стороне сервера и тем самым сократить прошедшее время для завершения всей работы?