jQuery ajax (jsonp) ignora un timeout e non attiva l'evento di errore


89

Per aggiungere un po 'di gestione degli errori di base, volevo riscrivere un pezzo di codice che utilizzava $ .getJSON di jQuery per estrarre alcune foto da Flickr. Il motivo per farlo è che $ .getJSON non fornisce la gestione degli errori né lavora con i timeout.

Dato che $ .getJSON è solo un wrapper intorno a $ .ajax, ho deciso di riscrivere la cosa e, sorpresa sorpresa, funziona perfettamente.

Ora però inizia il divertimento. Quando provo deliberatamente un 404 (modificando l'URL) o provochi il timeout della rete (non essendo collegato agli interweb), l'evento di errore non si attiva affatto. Non so cosa sto facendo di sbagliato. L'aiuto è molto apprezzato.

Ecco il codice:

$(document).ready(function(){

    // var jsonFeed = "http://api.flickr.com/services/feeds/photos_public.gne"; // correct URL
    var jsonFeed = "http://api.flickr.com/services/feeds/photos_public.gne_______"; // this should throw a 404

    $.ajax({
        url: jsonFeed,
        data: { "lang" : "en-us",
                "format" : "json",
                "tags" : "sunset"
        },
        dataType: "jsonp",
        jsonp: "jsoncallback",
        timeout: 5000,
        success: function(data, status){
            $.each(data.items, function(i,item){
                $("<img>").attr("src", (item.media.m).replace("_m.","_s."))
                          .attr("alt", item.title)
                          .appendTo("ul#flickr")
                          .wrap("<li><a href=\"" + item.link + "\"></a></li>");
                if (i == 9) return false;
            });
        },
        error: function(XHR, textStatus, errorThrown){
            alert("ERREUR: " + textStatus);
            alert("ERREUR: " + errorThrown);
        }
    });

});

Vorrei aggiungere che questa domanda è stata posta quando jQuery era alla versione 1.4.2

Risposte:


86

jQuery 1.5 e versioni successive hanno un supporto migliore per la gestione degli errori con le richieste JSONP. Tuttavia, è necessario utilizzare il $.ajaxmetodo anziché $.getJSON. Per me funziona:

var req = $.ajax({
    url : url,
    dataType : "jsonp",
    timeout : 10000
});

req.success(function() {
    console.log('Yes! Success!');
});

req.error(function() {
    console.log('Oh noes!');
});

Il timeout sembra fare il trucco e chiamare il gestore degli errori, quando non c'è richiesta riuscita dopo 10 secondi.

Ho anche scritto un post sul blog su questo argomento.


23
Sono oltremodo infastidito in questo momento dal fatto che ho sbattuto la testa contro questo problema per 2 giorni e ci vogliono alcune ricerche oscure su StackOverflow per scoprire che devo aggiungere un timeout a una richiesta interdominio per far scattare l'errore. Come può questo non essere menzionato nella documentazione di jQuery? Le richieste jsonp tra domini sono davvero così rare? In ogni caso grazie, grazie, grazie. +1
chrixian

Con questa soluzione non riesco a ottenere il codice di stato, ottengo invece "timeout". Quindi non posso sapere qual è l'errore effettivo e non posso gestirlo di proprietà.
Tarlog

10
@ Tarlog: è logico. Le richieste JSONP non sono richieste Ajax native, sono semplicemente <script>tag Javascript che vengono inseriti dinamicamente. L'unica cosa che puoi quindi sapere è che una richiesta non è riuscita, non quale fosse il codice di stato. Se desideri ottenere codici di stato, devi utilizzare le tradizionali richieste Ajax o altre tecniche interdominio.
Husky

1
Come ha detto @Husky, non puoi avere codici di stato con JSONP e credo che sia per questo che dovresti cercare di evitarlo. L'unico modo per ottenere un errore è impostare un timeout. Questo dovrebbe essere spiegato meglio nella documentazione di jQuery (come molte altre cose).
Alejandro García Iglesias

67

Questa è una limitazione nota con l'implementazione jsonp nativa in jQuery. Il testo seguente proviene da IBM DeveloperWorks

JSONP è una tecnica molto potente per la creazione di mashup, ma, sfortunatamente, non è un toccasana per tutte le tue esigenze di comunicazione tra domini. Presenta alcuni inconvenienti che devono essere presi in seria considerazione prima di impegnare risorse di sviluppo. Innanzitutto, non esiste alcuna gestione degli errori per le chiamate JSONP. Se l'inserimento dinamico dello script funziona, vieni chiamato; in caso contrario, non accade nulla. Semplicemente fallisce silenziosamente. Ad esempio, non sei in grado di rilevare un errore 404 dal server. Né puoi annullare o riavviare la richiesta. È tuttavia possibile eseguire il timeout dopo aver atteso un ragionevole periodo di tempo. (Le versioni future di jQuery potrebbero avere una funzione di interruzione per le richieste JSONP.)

Tuttavia, è disponibile un plug-in jsonp su GoogleCode che fornisce supporto per la gestione degli errori. Per iniziare, apporta le seguenti modifiche al tuo codice.

Puoi scaricarlo o semplicemente aggiungere un riferimento allo script al plug-in.

<script type="text/javascript" 
     src="http://jquery-jsonp.googlecode.com/files/jquery.jsonp-1.0.4.min.js">
</script>

Quindi modifica la tua chiamata ajax come mostrato di seguito:

$(function(){
    //var jsonFeed = "http://api.flickr.com/services/feeds/photos_public.gne"; // correct URL
    var jsonFeed = "http://api.flickr.com/services/feeds/photos_public.gne_______"; // this should throw a 404  
    $.jsonp({
        url: jsonFeed,
        data: { "lang" : "en-us",
                "format" : "json",
                "tags" : "sunset"
        },
        dataType: "jsonp",
        callbackParameter: "jsoncallback",
        timeout: 5000,
        success: function(data, status){
            $.each(data.items, function(i,item){
                $("<img>").attr("src", (item.media.m).replace("_m.","_s."))
                          .attr("alt", item.title)
                          .appendTo("ul#flickr")
                          .wrap("<li><a href=\"" + item.link + "\"></a></li>");
                if (i == 9) return false;
            });
        },
        error: function(XHR, textStatus, errorThrown){
            alert("ERREUR: " + textStatus);
            alert("ERREUR: " + errorThrown);
        }
    });
});

Grazie per te rispondi José! L'implementazione limitata di jsonp nativo in jQuery è stata la ragione per cui ho scelto $ .ajax. Tuttavia, sembra quindi che il problema non sia che $ .getJSON sia un wrapper intorno a $ .ajax ma dataType: jsonp. È corretto?
Matijs,

Non ho avuto tempo fino ad oggi. Tuttavia non funziona. Ricevo un errore, ma questo è tutto. Non so quale errore come errorThrown sia indefinito. Per ora mi limiterò a $ .ajax e alla mancanza di messaggi di errore. Javascript è un livello extra in cima alla pagina web per me. Se Flickr non funziona o genera messaggi di errore, per ora non ci resta che conviverci :) Grazie per il tuo suggerimento.
Matijs,

Quindi fondamentalmente il suggerimento di José di usare il plugin jsonp dovrebbe, se fatto correttamente, funzionare. Per ora, tuttavia, continuerò con il wrapper json di base per jQuery.
Matijs

Questo ha funzionato brillantemente per me una volta che ho colpito il muro con i limiti incorporati della gestione degli errori di jsonp
Jeremy Frey

7
Un altro sviluppatore salvato da questo suggerimento. Grazie!
chesterbr

12

Una soluzione se sei bloccato con jQuery 1.4:

var timeout = 10000;
var id = setTimeout( errorCallback, timeout );
$.ajax({
    dataType: 'jsonp',
    success: function() {
        clearTimeout(id);
        ...
    }
});

2
solo un'osservazione, è una soluzione valida per le persone bloccate con 1.4, ma imposta la variabile di timeout abbastanza alta, in modo da non avere problemi con i servizi web lenti :). se lo imposti a un secondo, ad esempio, puoi vedere cosa intendo, viene attivato il callback dell'errore, mentre dopo quel 1 secondo la tua chiamata viene ancora recuperata e chiama la funzione di successo. quindi tieni a mente di impostarlo abbastanza alto
Sander

@Sander la funzione di successo potrebbe anche essere chiamata se si ottiene una risposta dopo 10 secondi con un timeout di 5 secondi. Chiamare .abort()un jqXHR in sospeso dopo un periodo di timeout potrebbe essere un modo migliore.
Matijs

8

Questa potrebbe essere una limitazione "nota" di jQuery; tuttavia, non sembra essere ben documentato. Ho passato circa 4 ore oggi cercando di capire perché il mio timeout non funzionava.

Sono passato a jquery.jsonp e ha funzionato come un fascino. Grazie.


2

Sembra essere risolto a partire da jQuery 1.5. Ho provato il codice sopra e ottengo la richiamata.

Dico "sembra essere" perché la documentazione del callback dell'errore per jQuery.ajax () ha ancora la seguente nota:

Nota: questo gestore non viene chiamato per script interdominio e richieste JSONP.


1

Il plug-in jQuery jquery-jsonp menzionato nella risposta di Jose Basilio può ora essere trovato su GitHub .

Purtroppo la documentazione è alquanto spartana, quindi ho fornito un semplice esempio:

    $.jsonp({
        cache: false,
        url: "url",
        callbackParameter: "callback",
        data: { "key" : "value" },
        success: function (json, textStatus, xOptions) {
            // handle success - textStatus is "success"   
        },
        error: function (xOptions, textStatus) {
            // handle failure - textStatus is either "error" or "timeout"
        }
    });

Importante Includere callbackParameter nella chiamata $ .jsonp () altrimenti ho scoperto che la funzione di callback non viene mai inserita nella stringa di query della chiamata di servizio (attuale dalla versione 2.4.0 di jquery-jsonp).

So che questa domanda è vecchia, ma il problema della gestione degli errori utilizzando JSONP non è ancora "risolto". Si spera che questo aggiornamento possa aiutare gli altri poiché questa domanda è la prima classificata in Google per la "gestione degli errori jquery jsonp".


bella scoperta, e sorprendente questo thread è ancora vivo dopo tre anni ...
Matijs

1

Che ne dici di ascoltare l'evento onerror della sceneggiatura? Ho avuto questo problema e l'ho risolto applicando una patch al metodo di jquery in cui è stato creato il tag script. Ecco la parte interessante del codice:

// Attach handlers for all browsers
script.onload = script.onreadystatechange = function( _, isAbort ) {

    if ( isAbort || !script.readyState || /loaded|complete/.test( script.readyState ) ) {

        cleanup();

        // Callback if not abort
        if ( !isAbort ) {
            callback( 200, "success" );
        }
    }
};

/* ajax patch : */
script.onerror = function(){
    cleanup();

    // Sends an inappropriate error, still better than nothing
    callback(404, "not found");
};

function cleanup(){
    // Handle memory leak in IE
    script.onload = script.onreadystatechange = null;

    // Remove the script
    if ( head && script.parentNode ) {
        head.removeChild( script );
    }

    // Dereference the script
    script = undefined;
}

Cosa ne pensate di questa soluzione? È probabile che causi problemi di compatibilità?

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.