web-dev-qa-db-fra.com

jQuery ajax (jsonp) ignore un délai d'attente et ne déclenche pas l'événement d'erreur

Pour ajouter une gestion de base des erreurs, je voulais réécrire un morceau de code qui utilisait $ .getJSON de jQuery pour extraire certaines photos de Flickr. La raison pour cela est que $ .getJSON ne fournit pas de gestion des erreurs ou ne fonctionne pas avec des délais d'attente.

Étant donné que $ .getJSON n'est qu'un wrapper autour de $ .ajax, j'ai décidé de réécrire la chose et de surprendre la surprise, cela fonctionne parfaitement.

Maintenant, le plaisir commence. Lorsque je cause délibérément un 404 (en changeant l'URL) ou que le réseau expire (en n'étant pas connecté aux interwebs), l'événement d'erreur ne se déclenche pas du tout. Je ne sais pas ce que je fais mal. L'aide est très appréciée.

Voici le code:

$(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);
        }
    });

});

Je voudrais ajouter que cette question a été posée lorsque jQuery était à la version 1.4.2

89
Matijs

jQuery 1.5 et supérieur ont un meilleur support pour la gestion des erreurs avec les requêtes JSONP. Cependant, vous devez utiliser le $.ajax méthode au lieu de $.getJSON. Pour moi, cela fonctionne:

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

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

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

Le délai d'attente semble faire l'affaire et appeler le gestionnaire d'erreurs, lorsqu'il n'y a pas de demande réussie après 10 secondes.

J'ai également fait un peu blogpost sur ce sujet.

86
Husky

Il s'agit d'une limitation connue avec l'implémentation jsonp native dans jQuery. Le texte ci-dessous provient de IBM DeveloperWorks

JSONP est une technique très puissante pour créer des mashups, mais, malheureusement, ce n'est pas un remède universel pour tous vos besoins de communication inter-domaines. Elle présente certains inconvénients qui doivent être sérieusement pris en compte avant d'engager des ressources de développement. Tout d'abord, il n'y a pas de gestion des erreurs pour les appels JSONP. Si l'insertion de script dynamique fonctionne, vous êtes appelé; sinon, rien ne se passe. Il échoue silencieusement. Par exemple, vous n'êtes pas en mesure de détecter une erreur 404 sur le serveur. Vous ne pouvez pas non plus annuler ou redémarrer la demande. Vous pouvez toutefois expirer après avoir attendu un temps raisonnable. (Les futures versions de jQuery peuvent avoir une fonction d'abandon pour les requêtes JSONP.)

Cependant, il existe un plug-in jsonp disponible sur GoogleCode qui prend en charge la gestion des erreurs. Pour commencer, apportez simplement les modifications suivantes à votre code.

Vous pouvez le télécharger ou simplement ajouter une référence de script au plug-in.

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

Modifiez ensuite votre appel ajax comme indiqué ci-dessous:

$(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);
        }
    });
});
67
Jose Basilio

Une solution si vous êtes bloqué avec jQuery 1.4:

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

Cela peut être une limitation "connue" de jQuery; cependant, il ne semble pas bien documenté. J'ai passé environ 4 heures aujourd'hui à essayer de comprendre pourquoi mon délai d'attente ne fonctionnait pas.

Je suis passé à jquery.jsonp et cela a fonctionné comme un charme. Merci.

8
Marc

Semble être résolu à partir de jQuery 1.5. J'ai essayé le code ci-dessus et je reçois le rappel.

Je dis "semble être" parce que la documentation du rappel d'erreur pour jQuery.ajax () a toujours la note suivante:

Remarque: Ce gestionnaire n'est pas appelé pour les requêtes inter-domaines de script et JSONP.

2
Peter Tate

Le plugin jquery-jsonp jQuery mentionné dans Jose Basilio'sanswer peut maintenant être trouvé sur GitHub .

Malheureusement, la documentation est quelque peu spartiate, j'ai donc fourni un exemple simple:

    $.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"
        }
    });

Important Inclut le callbackParameter dans l'appel $ .jsonp () sinon j'ai trouvé que la fonction de rappel n'est jamais injectée dans la chaîne de requête de l'appel de service (à jour à partir de la version 2.4.0 de jquery-jsonp).

Je sais que cette question est ancienne, mais le problème de la gestion des erreurs à l'aide de JSONP n'est toujours pas "résolu". J'espère que cette mise à jour aidera les autres, car cette question est la mieux classée au sein de Google pour "la gestion des erreurs jquery jsonp".

1
OiDatsMyLeg

Qu'en est-il de l'écoute de l'événement onerror du script? J'ai eu ce problème et je l'ai résolu en corrigeant la méthode de jquery où la balise de script a été créée. Voici le morceau de code intéressant:

// 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;
}

Que pensez-vous de cette solution? Est-il susceptible de provoquer des problèmes de compatibilité?

1
Hadrien Milano