google-apps-script - manager - javascript google apps script
¿Qué sucede cuando "duermo" en GAS?(solución de límite de tiempo de ejecución) (1)
Detrás de esto (no tanto, lo admito ...) la pregunta graciosa es una pregunta real sobre una solución alternativa que uso sin entender realmente cómo funciona.
Primero una breve descripción de mi caso de uso, todo esto está sucediendo en un documento vinculado a UiApp que aparece en la barra lateral:
Tengo que crear y enviar por correo electrónico un par de cientos de documentos en una aplicación de combinación de correspondencia escrita en GAS. Tarda, por supuesto, demasiado tiempo para ser procesado en un lote sin alcanzar el límite de tiempo de ejecución de 5 minutos, así que probé algunas soluciones diferentes para realizar la tarea:
- use una marca de tiempo (almacenada en ScriptProperties) cuando comienzo el proceso y cuando alcanzo un valor predefinido cerca del límite, almaceno los valores actuales (punteros, vars útiles ...) y regreso a la interfaz de usuario pidiéndole al usuario que continúe ( o no). Eso funciona bastante bien, pero necesita una acción humana para completar toda la tarea.
- Así que configuro una solución usando un disparador de temporizador que creo en la primera llamada del administrador y este activador llama a la función de creación / envío del documento. Esto también funciona bien, pero la función llamada trigger no puede interactuar con la UI, ya que parece que solo las funciones del manejador pueden actualizar la UI. El problema es que no puedo mostrar el progreso ni mostrarlo fácilmente cuando finaliza el proceso.
- Entonces recordé una pequeña aplicación que escribí hace un tiempo solo por diversión: era un temporizador que usaba un checkBox como disparador de manejador de servidor (de una idea sugerida hace tiempo por Romain Vialard en el viejo foro de Google) y decidí probar esto. truco en mi proceso de envío de correo.
Funciona perfectamente, proceso 40 lotes de documentos en cada llamada (durante aproximadamente 3 minutos), luego paneo por un tiempo y empiezo de nuevo hasta que se termina. Cada llamada es desencadenada por el manejador de servidor vinculado checkBox, la casilla de verificación misma se cambia en la función de controlador, creando su propio desencadenador de esta manera.
Mi pregunta (finalmente ;-) es: saber que todo el proceso puede tomar de 30 a 60 minutos, ¿con qué precisión es posible? ¿Cómo / por qué se considera esta función del manejador de servidor como proceso múltiple ya que se crean desde el interior de la función misma?
Espero ser lo suficientemente claro, (lo cual dudo ya que es un poco confuso en mi mente :-)
Me uno debajo del código de la aplicación de prueba del reloj que me dio la idea, probablemente hará que las cosas sean más fáciles de entender.
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
}
La afirmación de que las llamadas a la función del manejador de servidor no son procesos independientes porque "se crean desde dentro de la función misma" no es del todo cierto.
Ha configurado un elemento chk1
con un controlador de servidor doSomething
. Siempre que CheckBox esté marcado, se envía un evento al servidor. (... y su secuencia de comandos está causando esos eventos con cada llamada a chk1.setValue()
) El código de la interfaz de usuario y el código de UI circundante se está ejecutando en su navegador - haga clic en "mostrar fuente" o use un explorador para ver qué se ha enviado a su navegador los servidores de google (Advertencia: está ofuscado. Pero es posible que reconozca algunas de sus cadenas y, a partir de ahí, su código del lado del cliente).
Esto es lo que nos dicen en la documentación para Class ServerHandler :
Cuando se invoca un ServerHandler, la función a la que se refiere se llama al servidor de Apps Script en un script "nuevo".
Esa es la clave para extender su tiempo de operación: cada evento enviado genera una invocación de doSomething()
en un contexto operativo completamente nuevo; es como si hubiera abierto el editor de scripts en un navegador diferente y hubiera hecho clic en "ejecutar" en su script. La secuencia de comandos "nueva" no tiene acceso a los valores var de la ejecución anterior ... pero también tiene su propio conjunto de restricciones operativas, incluidos los temporizadores.
PD: Debes asegurarte de que el controlador del lado del servidor es "seguro para subprocesos" usando Lock, ya que estás accediendo a recursos compartidos a los que se puede acceder mediante varias instancias de la devolución de llamada doSomething()
. En relación con eso, es posible alcanzar otro límite con este script:
Solo por diversión, comenté .setVisible(false)
en chk1
, por lo que el checkBox estaría visible. Luego hice clic rápidamente varias docenas de veces. La visualización de la hora se agotó y finalmente apareció el error anterior. (minutos más tarde) Es una situación artificial, por supuesto, pero sigue siendo un estado de error que se puede evitar fácilmente.
PPS: me pregunto si se podría utilizar la misma técnica para despachar múltiples manejadores paralelos del lado del servidor, y así reducir el tiempo transcurrido para completar todo el trabajo.