jQuery ajax(jsonp)会忽略超时,并且不会触发错误事件


89

为了添加一些基本的错误处理,我想重写一段代码,该代码使用jQuery的$ .getJSON从Flickr中提取一些照片。这样做的原因是$ .getJSON不提供错误处理或使用超时。

由于$ .getJSON只是$ .ajax的包装,因此我决定重写该内容并感到惊讶,它可以完美地工作。

现在,乐趣开始了。当我故意导致404(通过更改URL)或导致网络超时(由于未连接到互连网)时,错误事件根本不会触发。我对我做错了事感到茫然。非常感谢您的帮助。

这是代码:

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

});

我想补充一点,当jQuery在1.4.2版本时,这个问题被问到了。

Answers:


86

jQuery 1.5及更高版本对JSONP请求的错误处理提供了更好的支持。但是,您需要使用$.ajax方法而不是$.getJSON。对我来说,这有效:

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

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

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

当10秒后没有成功的请求时,超时似乎可以解决问题并调用错误处理程序。

我也对此主题做了一个小博文


23
我现在已经非常恼火了,因为我已经连续2天对这个问题进行了猛烈的抨击,并且在StackOverflow上进行了一些晦涩的搜索,发现我必须向跨域请求添加超时才能触发错误。jQuery文档中如何不能提及?跨域jsonp请求真的不常见吗?无论如何,谢谢,谢谢,谢谢。+1
基督教徒2011年

使用此解决方案,我无法获取状态代码,而将获得“超时”。所以我不知道实际的错误是什么,并且无法处理它的属性。
Tarlog 2011年

10
@Tarlog:这是合乎逻辑的。JSONP请求不是本机Ajax请求,它们只是<script>动态插入的Javascript 标记。因此,您唯一可以知道的是请求失败,而不是状态码是什么。如果要获取状态码,则需要使用传统的Ajax请求或其他跨域技术。
赫斯基

1
正如@Husky所说,JSONP不能包含状态代码,我相信这就是为什么应该避免使用它的原因。出现错误的唯一方法是超时。这应该在jQuery文档中得到更好的解释(与其他许多东西一样)。
亚历杭德罗·加西亚·伊格莱西亚斯

67

这是jQuery中本地jsonp实现的已知限制。下面的文本来自IBM DeveloperWorks

JSONP是用于构建混搭的一种非常强大的技术,但是,不幸的是,它不能解决所有跨域通信需求。它有一些缺点,在投入开发资源之前必须认真考虑。首先,JSONP调用没有错误处理。如果动态脚本插入有效,您将被呼叫;如果没有,则什么也不会发生。它只是默默地失败。例如,您不能从服务器捕获404错误。您也不能取消或重新启动请求。但是,您可以在等待一段合理的时间后超时。(未来的jQuery版本可能具有JSONP请求的中止功能。)

但是,GoogleCode上有一个jsonp插件,可提供对错误处理的支持。首先,只需对代码进行以下更改。

您可以下载它,也可以只向该插件添加脚本引用。

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

然后修改您的ajax调用,如下所示:

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

感谢您回答何塞!jQuery中本地jsonp的有限实现是我改用$ .ajax的原因。但是,看来问题出在不是$ .getJSON是$ .ajax的包装,而是dataType:jsonp。那是对的吗?
Matijs

直到今天还没有时间。但是,它不起作用。我遇到了一个错误,仅此而已。我不知道是什么错误,因为errorThrown是未定义的。现在,我将坚持使用$ .ajax和缺少错误消息的方法。对我来说,Javascript是网页顶部的另一层。如果Flickr关闭或抛出错误消息,我们现在只需要忍受它:)谢谢您的建议。
Matijs

因此,基本上,José使用jsonp插件的建议应该可以正常运行。但是现在,我将继续使用jQuery的基本json包装器。
Matijs

这工作出色,我一旦我击中内置的JSONP的错误处理的限制墙
杰里米·弗雷

7
另一个由此技巧保存的开发人员。谢谢!
切斯特布里

12

如果您坚持使用jQuery 1.4的解决方案:

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

2
只是说一句,它对于坚持1.4的人们是一个有效的解决方案,但是将您的超时变量设置得足够高,这样您就不会因为慢速的Web服务而感到麻烦:)。例如,如果将其设置为一秒钟,则可以看到我的意思,错误回调被触发,而在那一秒钟之后,您的调用仍然被获取并调用成功函数。因此,请记住将其设置得足够高
Sander 2012年

如果您在10秒后得到5秒超时的答案,则@Sander甚至可以调用成功函数。.abort()超时后调用未决的jqXHR可能是一种更好的方法。
Matijs

8

这可能是jQuery的“已知”限制;但是,它似乎没有很好的记录。今天我花了大约4个小时来了解为什么我的超时无法正常工作。

我切换到jquery.jsonp,它的工作就像一个魅力。谢谢。


2

似乎要从jQuery 1.5开始解决。我已经尝试了上面的代码,并且得到了回调。

我之所以说“似乎是”,是因为jQuery.ajax()的错误回调的文档仍然有以下注释:

注意:对于跨域脚本和JSONP请求,不会调用此处理程序。


1

Jose Basilio的 答案中提到的jquery-jsonp jQuery插件现在可以在GitHub找到

不幸的是,文档有些简陋,所以我提供了一个简单的示例:

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

重要提示 在调用$ .jsonp()中包括callbackParameter,否则我发现回调函数永远不会注入到服务调用的查询字符串中(当前为jquery-jsonp 2.4.0版)。

我知道这个问题很旧,但是使用JSONP处理错误的问题仍然没有解决。希望此更新对其他人有帮助,因为此问题在Google中“ jquery jsonp错误处理”排名最高。


很好的发现,而且令人惊奇的是,三年后,该线程仍然存在……
Matijs 2012年

1

监听脚本的onerror事件怎么样?我遇到了这个问题,并通过在创建脚本标签的jquery方法上打补丁来解决了这个问题。这是有趣的代码:

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

您如何看待该解决方案?有可能引起兼容性问题吗?

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.