2011-11-07 12 views
6

En un simple sitio web basado en Ajax estamos haciendo algunas peticiones HttpRequests de forma síncrona (me doy cuenta de que "Ajax sincrónico" es algo así como un oxímoron). La razón principal por la que esto se realiza de forma síncrona o asíncrona es para simplificar el modelo de programación para algunos de los involucrados (historia larga).Force UI repintado en Webkit (Safari y Chrome) justo antes de la solicitud Synchronous "Ajax"

De todos modos, queremos poder realizar un cambio de estilo (específicamente, superponer la pantalla con blanco semitransparente como lo hace la búsqueda de Google) justo antes de realizar la solicitud y luego quitarla cuando vuelvan los resultados. Básicamente, esto se parece a:

load:function(url) { 
    .... 
    busyMask.className="Shown"; //display=block; absolute positioned full screen semi-transparent 
    var dta=$.ajax({type:"GET",dataType:"json",url:url,async: false}).responseText; 
    busyMask.className="Hidden"; //sets display=none; 
    ... 
    return JSON.parse(dta); 
    } 

Es bien sabido unas peticiones síncronas cierran la interfaz de usuario. Así que no es sorprendente que la capa blanca nunca aparezca en Safari y Chrome (lo hace de forma interesante en Firefox). He intentado ralentizar la respuesta hacia abajo y usar una superposición de color rosa para que sea dolorosamente obvio, pero simplemente no actualizará la interfaz de usuario hasta que se complete la solicitud. Dejar la parte 'busyMask.className = "Hidden"' mostrará la máscara, pero solo después de que se complete la solicitud de ajax.

he visto muchos muchos trucos para forzar la interfaz de usuario para volver a pintar (por ejemplo Why HourGlass not working with synchronous AJAX request in Google Chrome?, http://ajaxian.com/archives/forcing-a-ui-redraw-from-javascript), pero todos ellos parecen estar en conjunción con el intento de mostrar las actualizaciones reales "permanentes" DOM ​​o de estilo, no con mostrar temporalmente una cambio de estilo mientras se realiza una solicitud sincrónica.

Entonces, ¿hay alguna manera de hacer esto o estoy luchando una batalla perdida? Puede ser que solo tengamos que pasar a solicitudes asincrónicas caso por caso para las solicitudes con peores resultados, lo que podría ser una forma decente para abordar el problema de la curva de aprendizaje ... Pero espero que haya un afuera la caja de respuesta aquí.

Respuesta

4

Ok a los efectos de esta pregunta, voy a ignorar la justificación de por qué necesita solicitudes XHR sincrónicas. Entiendo que a veces las limitaciones de trabajo no permiten el uso de la solución de mejores prácticas y, por lo tanto, "recuperamos" para hacer el trabajo. ¡Así que centrémonos en cómo obtener Ajax sincrónico con un trabajo visual actualizado para usted!

Bien, considerando que está utilizando jQuery para su solicitud de XHR, voy a suponer que está bien usar jQuery para mostrar/ocultar el indicador de carga y manejar cualquier problema de sincronización.

En primer lugar vamos a configurar un indicador de carga en nuestra marcado:

<div id="loading" style="display:none;">Loading...</div> 

Ahora vamos a crear algunos javascript:

// Show the loading indicator, then start a SYNCRONOUS ajax request 
$('#loading').show().delay(100).queue(function() { 
    var jqxhr = $.ajax({type:"GET",dataType:"json",url:"www.yoururl.com/ajaxHandler",async: false}).done(function(){ 
     //Do your success handling here 
    }).fail(function() { 
     //Do your error handling here 
    }).always(function() { 
     //This happens regardless of success/failure 
     $('#loading').hide(); 
    }); 
    $(this).dequeue(); 
}); 

En primer lugar, queremos mostrar nuestro indicador de carga y luego dar el navegador una demora de momento para volver a pintar antes de que comience nuestra solicitud XHR sincrónica. Al utilizar el método .queue() de jQuery, estamos poniendo nuestra llamada .ajax() en la cola fx predeterminada para que no se ejecute hasta que se complete el .delay(), lo que por supuesto no ocurre hasta que se complete el .show().

El método jQuery .show() cambia el estilo de visualización CSS del elemento objetivo para bloquearlo (o restaura su valor inicial si está asignado). Este cambio en CSS hará que el navegador vuelva a funcionar (también conocido como "volver a dibujar") tan pronto como sea posible. La demora asegura que podrá volver a fluir antes de la llamada ajax. La demora no es necesaria en todos los navegadores, pero no dolerá más que el número de milisegundos que especifique (como de costumbre, IE será el factor limitante aquí, los otros navegadores estarán contentos con un retraso de 1ms, IE quería algo un poco más significativo para volver a pintar).

Aquí hay una jsFiddle para poner a prueba en unos pocos navegadores: jsfiddle example

+0

"Ignoraré la justificación" - gracias, lo agradezco. Voy a digerir su respuesta en detalle pronto y ver si puedo aplicarlo a lo que estamos haciendo ... – jlarson

+0

Desafortunadamente, este enfoque no me va a ayudar porque todavía es esencialmente asincrónico: el código para manejar el éxito está desarticulado del código. eso hace la solicitud He agregado algunas aclaraciones en el bloque de código en mi publicación para ayudar a aclarar eso. Una vez más, dense cuenta de que esto no es en absoluto la mejor práctica ...pero manejar la respuesta en una función separada desde donde se realizó la solicitud puede ser demasiado engorroso para algunas personas – jlarson

+0

También me gustaría señalar que esta no es la primera vez que se usa la sincronización como una forma de simplificar el modelo de desarrollo, ver Meteor por ejemplo : http://devopsangle.com/2012/04/25/pushing-data-not-pages-is-the-new-model-for-application-development/ (aunque, por supuesto, muy controvertido) – jlarson

0

¿Por qué cree:

doSomethingBeforeRequest(); 
response = synchronousAjax(); 
doSomethingToTheDataAfterRequest(response); 

que mucho "más simple" que:

doSomethingBeforeRequest(); 
properAjax(onSuccess(response){ 
    doSomethingToTheDataAfterRequest(response); 
}; 

para su equipo ? No estoy tratando de discutir, pero estoy muy preocupado por la justificación ...

El único beneficio del código síncrono en el que puedo pensar es que guardas unas llaves; a costa de congelar el navegador.

Si el navegador no completa el repintado antes de la solicitud *, la única opción que se me ocurre es utilizar un retraso (como sugiere BenSwayne); lo que haría que el código sea tan complejo como la llamada asincrónica, y aún así hará que el navegador no responda durante la solicitud.

EDITAR (algún tipo de respuesta):

Desde JavaScript carece de hilos; tiempos de espera y llamadas ajax (que le permiten al navegador hacer otra cosa antes de que se ejecute, algo así como sleep() en un lenguaje de subprocesos se usa), es bastante fundamental para la forma de programar JavaScript. Sé que puede ser un poco una curva de aprendizaje al principio (sé que estaba confundido), pero realmente no hay una forma sensata de evitar aprenderlo.

Una situación que sé que la gente puede tener la tentación de hacer llamadas sincrónicas es cuando varias solicitudes deben hacerse en el servidor en secuencia; pero se puede hacer que asíncrono también, por los nidos de varias llamadas de la siguiente manera:

doSomethingBeforeRequest1(); 
ajax(onSuccess(response1){ 
    doSomethingToTheDataAfterRequest1(response1); 
    ajax(onSuccess(response2){ 
     doSomethingToTheDataAfterRequest2(response2); 
    }; 
}; 

Pero a menos que cada llamada es bastante lento para terminar y quiere indicar el progreso en cada paso o algo; Prefiero recomendar que crees un nuevo servicio para combinar las dos operaciones con una sola llamada. (Este servicio podría usar los dos servicios existentes en secuencia, si aún los necesita por separado en algunos otros casos).

(* Estoy más sorprendido de que Firefox no actualizar el dom ...)

+0

Para responder a su pregunta, principalmente, no existe tal cosa como "doSomethingBeforeRequest" y "doSomethingToTheDataAfterRequest". En cambio, hay un montón de código en una función que incluye variables locales, luego tenemos que tomar algunos datos y luego hacer un montón de cosas más. Para hacerlo por separado tendríamos que poner esos vars en algún lugar que podamos tomar más adelante, que no es ciencia espacial sino un modelo de programación diferente. Para _me_ no es realmente más difícil, pero no soy el único desarrollador ... Ojalá pudiéramos volver atrás y hacer las cosas de manera diferente desde el principio, pero por supuesto que no es fácil ... – jlarson

+0

... supongo usted está predicando al coro aquí, pero la clase de la escuela dominical necesita una versión más simple del sermón. – jlarson

0

He Maded algunas pruebas y vine con algunos puntos: http://jsfiddle.net/xVHWs/1/

cambiar el código para usar jQuery de hide() , show() o animate({ opacity: 'show' }, 'fast') y animate({ opacity: 'hide' }, 'fast') Si deja funciones sin un parámetro de tiempo o especifica un tiempo de 0 ms, Firefox mostrará la superposición y la ocultará, los otros navegadores la ejecutarán rápidamente para que la vea. Ponga un 100 milisegundo en llamadas show, hide o animate y lo verá.

+0

Buen intento, pero los cambios de la interfaz de usuario no se muestran hasta que se completa la solicitud. – jlarson

-1
$.ajaxSetup({async:false}); 

    busyMask.className="Shown"; //display=block; absolute positioned full screen semi-transparent 
    var dta=$.ajax({type:"GET",dataType:"json",url:url}).responseText; 
    busyMask.className="Hidden"; //sets display=none; 

$.ajaxSetup({async:true}); 
+0

Eso no funcionará en todos los navegadores. – jlarson

+0

cómo puedes decir eso. cualquier referencia. Supongo que funciona en todos los navegadores. ¿Qué parte crees que no funcionará? –

+0

Por experiencia y por investigación estoy seguro de que estoy en lo cierto. JavaScript en los navegadores tiene un solo subproceso por fotograma (a menos que involucres a los trabajadores). El código que ha dado tiene EXACTAMENTE el mismo efecto que el código que escribí (con asincronía dentro de las opciones pasadas a $ .ajax), y no funcionará EXACTAMENTE de la misma manera. ¿Lo has probado en todos los navegadores? – jlarson

Cuestiones relacionadas