2010-10-07 16 views
6

Estoy tratando de "transmisión" (de servidor a cliente) en Javascript por ajax (por XmlHttpRequest (= xhr). Estoy usando la función handleResponse modificada que se describe en Cross-browser implementation of "HTTP Streaming" (push) AJAX patternXmlHttpRequest.responseText durante la carga (readyState == 3) en Chrome

function handleResponse() { 
if (http.readyState != 4 && http.readyState != 3) 
    return; 
if (http.readyState == 3 && http.status != 200) 
    return; 
if (http.readyState == 4 && http.status != 200) { 
    clearInterval(pollTimer); 
    inProgress = false; 
} 
// In konqueror http.responseText is sometimes null here... 
if (http.responseText === null) 
    return; 

while (prevDataLength != http.responseText.length) { 
    if (http.readyState == 4 && prevDataLength == http.responseText.length) 
     break; 
    prevDataLength = http.responseText.length; 
    var response = http.responseText.substring(nextLine); 
    var lines = response.split('\n'); 
    nextLine = nextLine + response.lastIndexOf('\n') + 1; 
    if (response[response.length-1] != '\n') 
     lines.pop(); 

    for (var i = 0; i < lines.length; i++) { 
     // ... 
    } 
} 

if (http.readyState == 4 && prevDataLength == http.responseText.length) 
    clearInterval(pollTimer); 

inProgress = false; 
} 

con script php, lo que me vuelca los datos (sin ajax realmente vuelca los datos al navegador, mientras avanza a)

no tengo ningún problema en Firefox, pero Google Chrome e IE dame un responseText mientras está vacío xhr.readyState equivale a 3. Encontré ese problema descrito en Internet, pero no me dio una y solución.

¿Sabes cómo pasar por este problema de implementación en Chrome? (w3c dice, ese responseText no puede ser NULL en readyState == 3 - Chrome implementó esta regla, pero solo da una cadena vacía)

Y si no sabe, ¿conoce alguna solución funcional en algunos productos? (marcos opensource, bibliotecas, etc.)

Muchas gracias por sus ideas.

Editar: La solución consiste en la creación de marco flotante, llamar a la secuencia de comandos para IFRAME y datos ras aquí y datos de agarre mediante javascript de marco flotante. Pero esta no es una solución ajax. Realmente me gustaría ver la solución pura de Ajax.

+0

¿Estás seguro de que estás configurando un "Tipo de contenido" de "texto/simple" o "aplicación/x-javascript" cuando comienzas tu respuesta del servidor? Aparentemente, los navegadores Webkit insisten en eso, o podrían hacerlo. – Pointy

+0

Configuro el tipo de contenido como "application/x-www-form-urlencoded". Intenté - como dijiste - application/x-javascript, pero esto no funciona en absoluto. –

Respuesta

0

La URL está enviando la solicitud a - ¿es parte de su dominio?

Podría ser debido a same origin policy.

Consulte este question y possible ways to circumvent it (y este article).

+3

bien, si ese fuera el caso, ¿no enfrentará también un problema con FF? – anirvan

+0

@anirvan, sí. Tienes razón. Pero con el código no veo ninguna otra posibilidad (que yo sepa). – Nivas

+0

Gracias por responder :) –

0

una vez tuve este problema al usar safari (nunca probado con Chrome, tal vez había el mismo problema (cromo/safari ambos usan el mismo motor de renderizado (hasta donde yo sé) - no sé sobre el js- partes)). Nunca encontré una solución para evitar eso, pero debido a que era una aplicación pequeña en una intranet de toda la compañía, no era un gran problema no admitir safari (de todos modos, el navegador predeterminado era ff y funciona).

+0

Es tan triste para mí ... Implementé "degradación elegante", pero realmente quiero saber algo de solución, etc. Supongo que usar el flash como capa de transporte de servidor/cliente ... La solución limpia sin flash sería increíble ... –

+0

Apple Safari y Google Chrome solo usan el mismo motor de renderizado, Webkit, pero creo que hasta ahora son las similitudes. Google Chrome usa su propio motor de JavaScript nuevo llamado V8. – Frank

3

¿Ha considerado usar WebSockets o server-sent events?

Most major browsers ahora son compatibles con el protocolo WebSocket, aunque si su sitio necesita funcionar en IE 9 o anterior, o en Android Browser 4.3 o anterior, debería mantener el código que usa XMLHttpRequest como una alternativa.

Most of these browsers también apoyan una función llamada eventos enviados por el servidor, que a diferencia de WebSockets, se pueden implementar en el servidor mediante un daemon HTTP tradicional y un script CGI/PHP, aunque sólo proporciona una comunicación unidireccional.

Consulte también: WebSockets vs. Server-Sent events/EventSource

+1

Dulce futuro :) Pero está el último navegador ...> IE9 Websockets estará bien, y para mi propósito sería el más increíble Node.js :) –

+0

Pienso que el Sr. Moravec también tendría problemas con su FF3 , si el elige web sockets ahora. Según el artículo de Wikipedia sobre el tema, el soporte está en FF4, que está en fase beta ahora, por lo que pasará un tiempo antes de que todos lo tengan. Aún no he venido para probarlo, ahora soy un poco flojo y todavía no están distribuyendo Debs. No solo haga clic e instálelo con otras palabras. – Frank

1

trate de usar la propiedad responseStream/responseBody cuando en el IE. Estaba pensando en hacer algo similar una vez y me encontré con el mismo problema.Por desgracia tampoco lo son las especificaciones del W3C

http://www.w3.org/TR/XMLHttpRequest/#the-responsetext-attribute

+0

Lo probaría, pero IE terminaría con un error (ningún método o resultado es nulo) –

1

como yo lo entiendo, por lo que el texto parcial disponible en readyState 3 es un único no-estándar de Firefox comportamiento que simplemente no es posible emular directamente en otros navegadores, lo que es posible que desee en su lugar, realizar varias solicitudes secuenciales para fragmentos pequeños de datos en lugar de una solicitud de "transmisión"

2

Bueno, desafortunadamente, cada parte de XmlHttpRequest (o cualquier estándar web) no se implementa completamente en todos los navegadores. Sin embargo, tiene varias otras opciones para el flujo HTTP:
Wikipedia: Push technology
Wikipedia: Comet (programming) Wikipedia: Web Sockets (, visualización de baja experimental)

que vi en su comentario que le gustaría que fuera pura AJAX, pero me gusta sugerir posibles formas alternativas a las soluciones. Puede usar un JavaApplet siempre que sea posible o un objeto Flash. Para este último no necesitará un IDE ostentoso y costoso, puede usar haXe para crear archivos Flash/SWF y se sentirá muy cómodo con él ya que conoce JavaScript.

Aquí hay un Flash/Neko chat example que probablemente también pueda ser adoptado para otras plataformas y usos.

Le deseo la mejor de las suertes.

1

Esto funcionó para mí para Chrome, pero no IE:

[prueba.php]:

<?php 
Header('Content-type: text/plain'); 
while (1) { 
    echo str_pad('test: '.mt_rand(1000,9999), 2048, ' '); 
    flush(); 
    sleep(1); 
} 

[test.html]:

<!DOCTYPE html> 
<html lang="en"> 
    <head> 
     <title>Stream test</title> 
     <meta charset="UTF-8" /> 
     <script type="text/javascript"> 
      function xmlHttpRequest() { 
       return (function (x,y,i) { 
        if (x) return new x(); 
        for (i=0; i<y.length; y++) try { 
        return new ActiveXObject(y[i]); 
        } catch (e) {} 
       })(
        window.XMLHttpRequest, 
        ['Msxml2.XMLHTTP','Microsoft.XMLHTTP'] 
       ); 
      }; 
      function stream(url) { 
       // Declare the variables we'll be using 
       var xmlHttp = xmlHttpRequest(); 
       xmlHttp.open("GET", url, true); 
       var len = 0; 
       xmlHttp.onreadystatechange = function() { 
       if (xmlHttp.status == 200 && xmlHttp.readyState >=3) { 
        var text = xmlHttp.responseText; 
        text = text.substr(len, text.length-len); 
        len = xmlHttp.responseText.length; 
        console.log(text); 
       } 
       } 
       xmlHttp.send(null); 
      }   
      stream('/test.php'); 
     </script> 
    </head> 
    <body> 
    </body> 
</html> 

tu caso es distinto.

1

Como dijo Jaroslav Moravec, si configuras el tipo de contenido en el encabezado de la transmisión a application/x-javascript funciona en Safari, Chrome y Firefox.

No he probado IE.

16

Chrome tiene un error en el que solo aparecerá xhr.responseText después de que se haya recibido una determinada cantidad de bytes. Hay 2 maneras de evitar esto,

establecer el tipo de contenido del retorno a la "application/octet-stream"

o

Enviar un preludio de aproximadamente 2 kb para preparar el manejador.

Cualquiera de estos métodos deben hacer cromo poblar el campo responseText cuando readyState == 3.

Internet Explorer 7/8, por otra parte, no puede hacerlo, es necesario recurrir a la larga votación o utilizar el dominio cruz truco con XDomainRequest en IE8, a la MS

+0

Establecer el tipo de contenido no funciona, ¿2k es demasiado grande, alguna otra opción? – TomWan

+0

tipo de contenido de "application/octet-stream" o "application/x-javascript" no funciona para Chrome, 2k funciona. – pzy

1

Establecer el tipo de contenido de la vuelta a "application/octet-stream" como lo sugirió Andrew fue una gran solución. Además, debe usar XDomainRequest en IE.

Para leer los datos tal como vienen, solo debe usar un bucle infinito (que se detiene cuando readystate = 4 o XDomainRequest.onLoad ha sido llamado) con un tiempo de espera agotado.

Así es como yo lo haría:

var i = 0; 
var returnValue = function() { 
    if (!finished) { 
     setTimeout(returnValue, 100); 
    } 
    var resp = ajax.responseText; 
    var newI = resp.lastIndexOf(";") + 1; 
    if (newI > i) { 
     var lines = resp.substring(i, newI).split(";"); 
     for (var x = 0; x < lines.length; x++) { 
      eval(lines[x]); 
     } 
     i = newI; 
    } 
} 

Nota: algunos dicen usando eval es arriesgado, reclamo no es ahí donde cualquier riesgo realmente vienen.

3

Para ampliar la respuesta de Andrew, esta es la solución de varios navegadores que se me ocurrió.

funciona correctamente en el 99% de los navegadores, a saber IE ≥ 8, Chrome, Firefox y Safari, el envío de eventos incrementales tan pronto como se reciben datos por el navegador (pero ver las notas a continuación.)

if (/MSIE [8-9]/.test(navigator.appVersion)) { 
    var get = new XDomainRequest() 
    get.onprogress = handleData 
    get.onload = handleData 
} else { 
    var get = new XMLHttpRequest() 
    get.onreadystatechange = handleData 
} 
get.open('get', '/example/url') 
get.send() 

function handleData() { 
    if (get.readyState != null && (get.readyState < 3 || get.status != 200)) { 
     return 
    } 
    // process incremental data found in get.responseText 
} 

IE 8-9 comenzará a rellenar responseText después de 2kB de datos, por lo que si no está bien, debe enviar un relleno inicial de 2kB.

Chrome necesita eso, o Content-Type: application/octet-stream.

Cuestiones relacionadas