2012-07-02 23 views
8

Estoy teniendo un poco de pesadilla aquí, por lo que cualquier ayuda sería gratamente apreciada. En primer lugar, explicaré lo que estoy tratando de hacer:jQuery Long Polling (con servidor PHP)

Estoy tratando de implementar un sistema como el descrito aquí: https://stackoverflow.com/a/1086448/1034392 en mi servidor MAMP local usando el framework Yii. Tengo una función que comprueba si hay nuevas notificaciones en la base de datos; si es así, las analiza y json las codifica. Tengo esta función llamada en un ciclo while cada 5 segundos.

Así: va a la/usuario/unreadNotifications desencadena la siguiente

Yii::log('test'); // to check it's getting called 

    $this->layout=false; 

    header('Content-Type: application/json'); 

    // LONG POLLING 
    while (Yii::app()->user->getNotifications() == null) { 
     sleep(5); 
    } 

    echo Yii::app()->user->getNotifications(); // prints out json if new notification 

    Yii::app()->end(); 

    return; 

Esto funciona bien - ido al enlace en el navegador y verificado respuesta JSON - todo bien.

He intentado todo tipo de cosas jQuery para que funcione ... La ÚNICA forma en que he encontrado que funciona es usando $.ajax con el tipo POST pero SÓLO cuando hay una notificación de espera (por lo que se devuelve algo de json). $.get o $.post se "cancela" (se muestra en firebug) pero se llama a la URL (porque puedo ver que el archivo de registro está actualizado) - impar.

Mi configuración original utilizando $.get es:

 <script type="text/javascript"> 
      function notificationPoll() { 
       $.get('<?php echo Yii::app()->createUrl('user/unreadNotifications') ?>','', function(result) { 
        $.each(result.events, function(events) { 
         alert('New Notification!'); 
        }); 
        notificationPoll(); 
       }, 'json'); 
      } 
     </script> 

     <script type="text/javascript"> 
      $(document).ready(function() { 
       $.ajaxSetup({ 
        timeout: 60 //set a global ajax timeout of a minute 
       }); 
       notificationPoll(); 
      }); 
     </script> 

Esto se pone "abortado" por alguna razón. Lo he intentado con 'jsonp' aunque no es una solicitud de CORS ... pero tampoco funciona.

Parece que no se puede ir a ninguna parte con esto! ¿Alguien puede participar?

Muchas gracias

+0

Se olvidó de mencionar: cuando uso $ .ajax con tipo POST y NO hay que mostrar ninguna notificación, cualquier página de sitio que tenga el código jQuery solo no se carga hasta que se reciba una notificación. –

+0

Error de registro de consola ?? pegarlo .. –

Respuesta

2

¿Qué hace el getNotifications regresan si no hay notificaciones? jQuery espera que se devuelva un formato JSON, pero cuando solo hace eco de una cadena vacía, la solicitud falla ya que el formato de la respuesta no es un JSON. Asegúrese de repetir la cadena JSON cada vez.

+0

gracias Andy. Actualmente getNotifications() devuelve null si no hay notificaciones, por lo que user/unreadNotifications simplemente realiza un bucle y en realidad no devuelve nada hasta que haya una nueva notificación. –

+0

debería simplemente agotar el tiempo de espera, sin respuesta. Quizás una mejor estructura $ .ajax sería más fácil de depurar, con tiempo de espera personal, éxito, pruebe {parse json, data_available} catch (e) {}, data_available: procese datos JSON, falle – Waygood

+0

Ok - parece que lo he reducido ¡finalmente! Cuando llamo $ .ajaxSteup() y establezco el tiempo de espera ... ¿está configurando el tiempo de espera en milisegundos? Subí eso a 60000 (60 segundos) y ahora el $ .get no 'aborta'. Sin embargo, un último problema: cargar una página inicialmente está bien, sin embargo, navegar a otra página simplemente se atasca. ¿Hay alguna manera de cerrar la solicitud GET de procesamiento al salir de la página? (Supongo que eso está causando que la página siguiente no se cargue ...) –

3

Qué tal esto. Supongo que el $. (Get) está en una función llamada notificationPoll(); que se vuelve a llamar una vez completado.

$.ajax({ 
    url: event_feed_href, 
    async: false, 
    timeout: 60000, 
    done: function(data) { 
      var got_json=false; 
      try { 
       var json = JSON.parse(data); 
       got_json=true; 
      } 
      catch(e) { 
       // failed to return JSON data 
       alert('Wierd!'); 
      } 
      if(got_json) { 
       // process json data 
       alert('New Notification!'); 
      } 
     }, 
    always: function() { 
      notificationPoll(); 
     } 
    }); 

He usado hecho y siempre aquí como dice jQuery se deprecia el éxito: Falla:

+0

El sondeo largo debe realizarse sin tiempo de espera del cliente. El tiempo de espera se puede usar solo para errores de red y vuelva a intentar la conexión. – Deep

3

usted debe estar seguro de que la función termina dentro de un tiempo razonable. Lo que podría hacer es esto:

$ttl = 10; 
while ($ttl--) { 
    $json = Yii::app()->user->getNotifications(); 
    if (null != $json) { 
     break; 
    } 
    sleep(1); 
} 
if (null == $json) { 
    $json = json_encode(array(
     'nothing' => true 
    )); 
} 
header('Content-Type: application/json'); 
echo $json; 

Yii::app()->end(); 
return; 

Configura la función de sondeo como un temporizador usando setInterval(). La función se llamará ahora todos, por ejemplo, 10 segundos, y es posible que tenga que configurar un semáforo para evitar que sea llamado antes de la iteración anterior ha vuelto:

var timer = setInterval(
    function() { 
     if (this.calling) { 
      return; 
     } 
     var fn = this; 
     fn.calling = true; 
     $.post(url) 
     .done(function(data) { 
      .. 
     }) 
     .always(function() { 
      fn.calling = false; 
     }); 
    }, 
    10000 
); 

Entonces la función de votación en AJAX necesita para comprobar (en el .done()) de devolución de llamada) que la notificación está ahí:

function(data) { 
    if (data.hasOwnProperty('nothing')) { 
     alert('No notifications'); 
     return; 
    } 
    console.log(data); 
    ... 
} 

Ahora, una cosa importante es lo que hace su mirada de notificación como. Aquí he supuesto que es una cadena codificada en JSON. Pero si se trata de una matriz u objeto que devuelve la función Yii, debe controlar su codificación.Esto podría ser incluso más limpio, sin ningún tipo de SI:

header('Content-Type: ... 
die(json_encode(
    array(
     'status'   => 'success', 
     'notification' => $json /* This is NULL or an array */ 
    ) 
    // Javascript side we check that data.notification is not null. 
)); 

La decodificación ya está en manos de jQuery, por lo que los "datos" variables anteriormente ya habrá un objeto Javascript, y que no será necesario que llame JSON.parse. Sin embargo, puede verificar que dataes un objeto y que tiene las propiedades esperadas. Eso te advertirá de cualquier error.

Para manejar la navegación a otra página, puede almacenar el setInterval() -dotificador de ID de la función de sondeo de Javascript en una variable global, y eliminar el temporizador cuando la página llama a onUnload() para desactivar el sondeo.

1

Estoy interesado en el aborto del código y es posible que haya una respuesta. Puede suceder por varias razones

  • Query.get failed. La llamada Ajax no funciona por ningún motivo. Error de sintáxis.
  • Mala sintaxis de JavaScript. Por ejemplo: el código en el controlador de éxito espera la propiedad de eventos en el argumento de entrada que puede no ser verdadera.

Para jQuery.get, como se sugiere en la jQuery.get (https://api.jquery.com/jQuery.get/) la documentación, la función puede fallar en silencio, voy a sugerir a utilizar .ajaxError para ver si el get está fallando.

Si una solicitud con jQuery.get() devuelve un código de error, fallará en silencio a menos que el script también haya llamado al método global .ajaxError(). Alternativamente, a partir de jQuery 1.5, el método .error() del objeto jqXHR devuelto por jQuery.get() también está disponible para el manejo de errores.

Para el error de sintaxis de Javascript, sugeriré que revise el depurador de Firebug. Agregue un punto de interrupción en la primera línea en el controlador de éxito y luego pase cada línea desde allí. Es tedioso, pero funciona.