2010-04-14 16 views
17

Bien, entonces aprecio que Javascript no sea C# o PHP, pero sigo volviendo a un problema en Javascript, no con JS sino con mi uso.¿Cómo se hace que el código javascript se ejecute * en orden *

que tienen una función:

function updateStatuses(){ 

showLoader() //show the 'loader.gif' in the UI 

updateStatus('cron1'); //performs an ajax request to get the status of something 
updateStatus('cron2'); 
updateStatus('cron3'); 
updateStatus('cronEmail'); 
updateStatus('cronHourly'); 
updateStatus('cronDaily'); 

hideLoader(); //hide the 'loader.gif' in the UI 

} 

La cosa es que, debido al deseo ardiente de Javascript para ir por delante en el código, el cargador nunca aparece porque la función 'hideLoader' se ejecuta inmediatamente después.

¿Cómo puedo solucionar esto? O en otras palabras, ¿cómo puedo hacer que una función javascript se ejecute en el orden en que la escribo en la página ...

+0

The Promise es el mejor objeto para manejar todas las solicitudes asincrónicas. [corregir las solicitudes de sincronización con el objeto Promesa] (https://stackoverflow.com/a/47719849/7487135) –

Respuesta

14

El problema ocurre porque AJAX está en su naturaleza asincrónica. Esto significa que las llamadas updateStatus() se ejecutan en orden, pero se devuelven inmediatamente y el intérprete JS llega al hideLoader() antes de que los datos se recuperen de las solicitudes AJAX.

Debe realizar el hideLoader() en un evento donde las llamadas AJAX hayan finalizado.

+0

Exactamente, JS no espera a que las tareas updateStatus finalicen antes de iniciar las siguientes tareas. Si cron1 tiene que finalizar antes de que arranque cron2, etc. Deberá configurar cada updateStatus para que se ejecute en Éxito de la llamada anterior. – MindStalker

2

Esto no tiene nada que ver con el orden de ejecución del código.

La razón por la que la imagen del cargador nunca aparece, es que la interfaz de usuario no se actualiza mientras se ejecuta su función. Si realiza cambios en la IU, no aparecerán hasta que salga de la función y devuelva el control al navegador.

Se puede utilizar un tiempo de espera después de fijar la imagen, dando al navegador una oportunidad para actualizar la interfaz de usuario antes de iniciar el resto del código:

function updateStatuses(){ 

    showLoader() //show the 'loader.gif' in the UI 

    // start a timeout that will start the rest of the code after the UI updates 
    window.setTimeout(function(){ 
    updateStatus('cron1'); //performs an ajax request to get the status of something 
    updateStatus('cron2'); 
    updateStatus('cron3'); 
    updateStatus('cronEmail'); 
    updateStatus('cronHourly'); 
    updateStatus('cronDaily'); 

    hideLoader(); //hide the 'loader.gif' in the UI 
    },0); 
} 

hay otro factor que también puede hacer que el código parecen ejecutar fuera de servicio. Si sus solicitudes AJAX son asincrónicas, la función no esperará las respuestas. La función que se ocupa de la respuesta se ejecutará cuando el navegador reciba la respuesta. Si desea ocultar la imagen del cargador después de que se haya recibido la respuesta, deberá hacerlo cuando se ejecute la última función del controlador de respuestas. Dado que las respuestas no tienen que llegar en el orden en que envió las solicitudes, necesitaría contar cuántas respuestas debe conocer cuando llegue la última.

+0

Gracias a Guffa y al Dr. Sbaitso, entiendo mucho más que sé lo que está pasando bajo el capó, voy a probar algunas de sus sugerencias y ver qué puedo hacer para trabajar. –

1

instalar Firebug, a continuación, añadir una línea como esta en cada uno de showLoader, updateStatus y hideLoader:

Console.log("event logged"); 

Se ven en la ventana de la consola las llamadas a su función, y que será el fin . La pregunta es, ¿qué hace tu método "updateStatus"?

Es de suponer que inicia una tarea en segundo plano, luego regresa, por lo que llegará a la llamada a hideLoader antes de que finalice cualquiera de las tareas en segundo plano. Su biblioteca Ajax probablemente tiene una devolución de llamada "OnComplete" o "OnFinished" - llame al siguiente updateStatus desde allí.

+0

el método updateStatus activa una llamada jquery ajax. La razón por la que no puedo ocultar el cargador en la devolución de llamada 'Onsuccess' es porque necesito esperar a que se completen varias llamadas. Pero usando el enfoque 'contador' dado a continuación, tengo una solución –

12

Tiene que pensar en JavaScript como un evento en lugar de un procedimiento si está haciendo la programación AJAX. Tienes que esperar hasta que la primera llamada se complete antes de ejecutar la segunda. La forma de hacerlo es vincular la segunda llamada a una devolución de llamada que se activa cuando termina la primera.Sin saber más sobre el funcionamiento interno de la biblioteca de AJAX (esperemos que estés usando una biblioteca) que no puedo decir cómo hacer esto, pero probablemente se verá algo como esto:

showLoader(); 

    updateStatus('cron1', function() { 
    updateStatus('cron2', function() { 
     updateStatus('cron3', function() { 
     updateStatus('cronEmail', function() { 
      updateStatus('cronHourly', function() { 
      updateStatus('cronDaily', funciton() { hideLoader(); }) 
      }) 
     }) 
     }) 
    }) 
    }) 
}); 

La idea es , updateStatus toma su argumento normal, más una función de devolución de llamada para ejecutar cuando termina. Es un patrón razonablemente común pasar una función para ejecutar onComplete en una función que proporciona dicho gancho.

actualización

Si estás usando jQuery, se puede leer en $.ajax() aquí: http://api.jquery.com/jQuery.ajax/

Su código probablemente se ve algo como esto:

function updateStatus(arg) { 
    // processing 

    $.ajax({ 
    data : /* something */, 
    url : /* something */ 
    }); 

    // processing 
} 

Usted puede modificar su funciones para tomar una devolución de llamada como su segundo parámetro con algo como esto:

function updateStatus(arg, onComplete) { 
    $.ajax({ 
    data : /* something */, 
    url : /* something */, 
    complete : onComplete // called when AJAX transaction finishes 
    }); 

}

+0

Hola Meagar, estoy usando jquery: ¿cuál sería la mejor forma de implementar esto? –

5

tenemos algo similar en uno de nuestros proyectos, y hemos resuelto mediante el uso de un contador. Si aumenta el contador para cada llamada al updateStatus y lo reduce en la función de respuesta de la solicitud AJAX (depende de la biblioteca AJAX JavaScript que esté utilizando)

Una vez que el contador llega a cero, todas las solicitudes AJAX se completan y usted puede llame al hideLoader().

He aquí una muestra:

var loadCounter = 0; 

function updateStatuses(){ 
    updateStatus('cron1'); //performs an ajax request to get the status of something 
    updateStatus('cron2'); 
    updateStatus('cron3');  
    updateStatus('cronEmail'); 
    updateStatus('cronHourly'); 
    updateStatus('cronDaily'); 
} 

function updateStatus(what) { 
    loadCounter++; 

    //perform your AJAX call and set the response method to updateStatusCompleted() 
} 

function updateStatusCompleted() { 
    loadCounter--; 
    if (loadCounter <= 0) 
     hideLoader(); //hide the 'loader.gif' in the UI 
} 
+0

Hola, Prutswonder, ¡probé esto y funciona! Me gusta la solución –

+3

Ed, si le gusta una de estas soluciones, ¿podría marcar su elección de la mejor solución haciendo clic en la casilla de verificación junto a la respuesta de esa persona? –

+0

+1 Probablemente mejor que mi solución para una serie de devoluciones de llamada tan anidadas – meagar

0

movimiento del updateStatus llama a otra función. hacer una llamada setTimeout con la nueva función como un objetivo.

si sus solicitudes ajax son asincrónicas, debe tener algo para rastrear cuáles se han completado. cada método de devolución de llamada puede establecer una bandera "completa" en algún lugar para sí mismo, y verifica si es la última en hacerlo. si es así, pídales que llamen a hideLoader.

1

Como han señalado otros, no desea realizar una operación sincrónica. Embrace Async, eso es lo que significa A en AJAX.

Me gustaría mencionar una excelente analogía en sincronizar v/s async. Puedes leer el entire post on the GWT forum, solo estoy incluyendo las analogías relevantes.

Imagínese si se quiere ...

Usted está sentado en el sofá viendo la televisión , y sabiendo que usted está fuera de cerveza, le pida a su cónyuge para complacer correr a la tienda de licores y traerte un poco. Tan pronto como vea su cónyuge salga por la puerta delantera, se levanta del sofá y entra en la cocina y abre el refrigerador . Para su sorpresa, ¡no hay cerveza !

Bueno, por supuesto, no hay cerveza, su cónyuge todavía está en el viaje a la licorería .Tienes que esperar hasta que [s] regrese antes de que puedas esperar para tomar una cerveza.

¿Pero, dices que lo quieres sincrónico? Imaginemos de nuevo ...

... cónyuge sale por la puerta ... ahora, todo el mundo a su alrededor se detiene, no llegan a la respiración, abrir la puerta , o terminar de ver su muestre mientras [s] corre por la ciudad al traiga su cerveza. Que acaba de llegar a sentarse no mover un músculo, y poniendo azul hasta que pierda la consciencia ... despertar alguna tiempo indefinido tarde rodeado de técnicos de emergencias médicas y un cónyuge diciendo oh, bueno, yo tengo su cerveza.

Eso es exactamente lo que sucede cuando insistes en hacer una llamada a un servidor síncrono.

10

I piensa todo lo que necesita hacer es tener esto en su código:

async: false, 

Así que su llamada Ajax se vería así:

jQuery.ajax({ 
      type: "GET", 
      url: "something.html for example", 
      dataType: "html", 
      async: false, 
      context: document.body, 
      success: function(response){ 

       //do stuff here 

      }, 
      error: function() { 
       alert("Sorry, The requested property could not be found."); 
      } 
     }); 

Obviamente algunos de esta necesidad de cambiar para XML , JSON etc. pero el async: false, es el punto principal aquí que le dice al motor JS que espere hasta que la llamada exitosa haya retornado (o haya fallado dependiendo) y luego continúe. Recuerda que hay una desventaja en esto, ¡y eso significa que toda la página deja de responder hasta que el ajax regrese! usualmente en milisegundos, lo cual no es una gran oferta pero PODRÍA tomar más tiempo.

Esperanza esta es la respuesta correcta y le ayuda :)

+0

Este fue el truco. Solución simple y concisa. Muchas gracias. – darksoulsong

+0

Aunque funcionó, esta no es una respuesta increíble. Desea que JS sea asincrónico. Hay muchas ventajas y escenarios en los que desea que funcione, y desactivarlo para toda su aplicación es bastante raro. Recomendaría utilizar una biblioteca de promesas en su lugar. Aquí hay un gran tutorial. http://dailyjs.com/2014/02/20/promises-in-detail/ – Codex

0

Una de las mejores soluciones para el manejo de todas las solicitudes asíncronas es el 'Promise'.
El objeto Promesa representa la finalización (o falla) eventual de una operación asincrónica.

Ejemplo:

let myFirstPromise = new Promise((resolve, reject) => { 
    // We call resolve(...) when what we were doing asynchronously was successful, and reject(...) when it failed. 
    // In this example, we use setTimeout(...) to simulate async code. 
    // In reality, you will probably be using something like XHR or an HTML5 API. 
    setTimeout(function(){ 
    resolve("Success!"); // Yay! Everything went well! 
    }, 250); 
}); 

myFirstPromise.then((successMessage) => { 
    // successMessage is whatever we passed in the resolve(...) function above. 
    // It doesn't have to be a string, but if it is only a succeed message, it probably will be. 
    console.log("Yay! " + successMessage); 
}); 

Promise

Si usted tiene 3 funciones asíncronas y espera que funcione en orden, hace de la siguiente manera:

let FirstPromise = new Promise((resolve, reject) => { 
    FirstPromise.resolve("First!"); 
}); 
let SecondPromise = new Promise((resolve, reject) => { 

}); 
let ThirdPromise = new Promise((resolve, reject) => { 

}); 
FirstPromise.then((successMessage) => { 
    jQuery.ajax({ 
    type: "type", 
    url: "url", 
    success: function(response){ 
     console.log("First! "); 
     SecondPromise.resolve("Second!"); 
    }, 
    error: function() { 
     //handle your error 
    } 
    });   
}); 
SecondPromise.then((successMessage) => { 
    jQuery.ajax({ 
    type: "type", 
    url: "url", 
    success: function(response){ 
     console.log("Second! "); 
     ThirdPromise.resolve("Third!"); 
    }, 
    error: function() { 
     //handle your error 
    } 
    });  
}); 
ThirdPromise.then((successMessage) => { 
    jQuery.ajax({ 
    type: "type", 
    url: "url", 
    success: function(response){ 
     console.log("Third! "); 
    }, 
    error: function() { 
     //handle your error 
    } 
    }); 
}); 

Con este enfoque, puede manejar todas las operaciones asincrónicas como lo desee.

Cuestiones relacionadas