¿Seguridad de subprocesos en Javascript?


Tengo una función llamada save(), esta función recoge todas las entradas de la página, y realiza una llamada AJAX al servidor para guardar el estado del trabajo del usuario.

Save() se llama actualmente cuando un usuario hace clic en el botón guardar, o realiza alguna otra acción que requiere que tengamos el estado más actual en el servidor (generar un documento desde la página, por ejemplo).

Estoy agregando la capacidad de guardar automáticamente el trabajo del usuario de vez en cuando. Primero me gustaría evitar que se ejecuten al mismo tiempo un guardado automático y un guardado generado por el usuario. Así que tenemos el siguiente código (estoy cortando la mayor parte del código y esto no es un 1:1, pero debería ser suficiente para transmitir la idea):

var isSaving=false;
var timeoutId;
var timeoutInterval=300000;
function save(showMsg)
{
  //Don't save if we are already saving.
  if (isSaving)
  { 
     return;
  }
  isSaving=true;
  //disables the autoSave timer so if we are saving via some other method
  //we won't kick off the timer.
  disableAutoSave();

  if (showMsg) { //show a saving popup}
  params=CollectParams();
  PerformCallBack(params,endSave,endSaveError);

}
function endSave()
{  
    isSaving=false;
    //hides popup if it's visible

    //Turns auto saving back on so we save x milliseconds after the last save.
    enableAutoSave();

} 
function endSaveError()
{
   alert("Ooops");
   endSave();
}
function enableAutoSave()
{
    timeoutId=setTimeOut(function(){save(false);},timeoutInterval);
}
function disableAutoSave()
{
    cancelTimeOut(timeoutId);
}

Mi pregunta es si este código es seguro? ¿Los principales navegadores permiten que solo se ejecute un subproceso a la vez?

Uno pensaba que tenía que sería peor que el usuario haga clic en guardar y obtener ninguna respuesta porque estamos automático (Y sé cómo modificar el código para manejar esto). ¿Alguien vio algún otro problema aquí?

Author: JoshBerke, 2010-02-12

5 answers

JavaScript en los navegadores es un subproceso único. Solo estarás en una función en cualquier momento. Las funciones se completarán antes de que se ingrese la siguiente. Puede contar con este comportamiento, por lo que si está en su función save(), nunca volverá a ingresarla hasta que la actual haya terminado.

Donde esto a veces se vuelve confuso (y sin embargo sigue siendo cierto) es cuando tiene solicitudes de servidor asíncronas (o setTimeouts o setIntervals), porque entonces se siente como sus funciones se están intercalando . No lo son.

En su caso, si bien dos llamadas save() no se superpondrán entre sí, el guardado automático y el guardado del usuario podrían ocurrir de forma consecutiva.

Si solo desea que se realice un guardado al menos cada x segundos, puede hacer un setInterval en su función de guardado y olvidarse de ello. No veo la necesidad de la bandera isSaving.

Creo que su código podría simplificarse mucho:

var intervalTime = 300000;
var intervalId = setInterval("save('my message')", intervalTime);
function save(showMsg)
{
  if (showMsg) { //show a saving popup}
  params=CollectParams();
  PerformCallBack(params, endSave, endSaveError);

  // You could even reset your interval now that you know we just saved.
  // Of course, you'll need to know it was a successful save.
  // Doing this will prevent the user clicking save only to have another
  // save bump them in the face right away because an interval comes up.
  clearInterval(intervalId);
  intervalId = setInterval("save('my message')", intervalTime);
}

function endSave()
{
    // no need for this method
    alert("I'm done saving!");
}

function endSaveError()
{
   alert("Ooops");
   endSave();
}
 42
Author: Jonathon Faust,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-08-21 13:43:52

Todos los navegadores principales solo admiten un hilo javascript (a menos que utilice web workers) en una página.

Las peticiones XHR pueden ser asíncronas. Pero siempre y cuando deshabilite la capacidad de guardar hasta que la solicitud actual para guardar regrese, todo debería funcionar bien.

Mi única sugerencia, es asegurarse de indicar al usuario de alguna manera cuando se produce un guardado automático (desactivar el botón guardar, etc.).

 7
Author: mikefrey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-02-12 17:16:00

Todos los principales navegadores actualmente ejecución de javascript de un solo hilo (simplemente no use web workers ya que algunos navegadores admiten esta técnica!), por lo que este enfoque es seguro.

Para un montón de referencias, ver Es JavaScript Multithreaded ?

 2
Author: Jeff Sternal,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:13:24

Parece seguro para mí. Javascript es un subproceso único (a menos que esté utilizando webworkers)

No es del todo sobre el tema, pero este post de John Resig cubre javascript threading y temporizadores: http://ejohn.org/blog/how-javascript-timers-work /

 2
Author: David Murdoch,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-02-12 17:16:13

Creo que la forma en que lo está manejando es la mejor para su situación. Mediante el uso de la bandera que está garantizando que las llamadas asíncronas no se superponen. También he tenido que lidiar con llamadas asíncronas al servidor y también he usado algún tipo de bandera para evitar la superposición.

Como ya han señalado otros, JavaScript es un subproceso único, pero las llamadas asíncronas pueden ser complicadas si esperas que las cosas digan lo mismo o no ocurran durante el viaje de ida y vuelta al servidor.

Uno la cosa, sin embargo, es que no creo que realmente necesite desactivar el guardado automático. Si el guardado automático intenta ocurrir cuando un usuario está guardando, entonces el método guardar simplemente regresará y no pasará nada. Por otro lado, está desactivando y volviendo a habilitar innecesariamente el guardado automático cada vez que se activa el guardado automático. Yo recomendaría cambiar a setInterval y luego olvidarlo.

También, soy un estricto para minimizar las variables globales. Probablemente me refactorizar el código como esto:

var saveWork = (function() {
  var isSaving=false;
  var timeoutId;
  var timeoutInterval=300000;
  function endSave() {  
      isSaving=false;
      //hides popup if it's visible
  }
  function endSaveError() {
     alert("Ooops");
     endSave();
  }
  function _save(showMsg) {
    //Don't save if we are already saving.
    if (isSaving)
    { 
     return;
    }
    isSaving=true;

    if (showMsg) { //show a saving popup}
    params=CollectParams();
    PerformCallBack(params,endSave,endSaveError);
  }
  return {
    save: function(showMsg) { _save(showMsg); },
    enableAutoSave: function() {
      timeoutId=setInterval(function(){_save(false);},timeoutInterval);
    },
    disableAutoSave: function() {
      cancelTimeOut(timeoutId);
    }
  };
})();

No tienes que refactorizarlo así, por supuesto, pero como dije, me gusta minimizar los globales. Lo importante es que todo funcione sin deshabilitar y volver a habilitar autosave cada vez que guarde.

Editar: Forgot tuvo que crear una función de guardado privado para poder hacer referencia desde enableAutoSave

 1
Author: Bob,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-02-12 21:36:44