我执行多个jsonp请求时都遇到了jQuery问题,所有这些请求都使用相同的jsonpCallback函数。似乎只有那些回调函数之一被触发。JSONP请求是否以某种方式相互覆盖?
下面是对github进行2个jsonp请求的示例,即使两个萤火虫都表明它们都返回了,回调函数getName仅针对其中一个调用:
getName
function getName(response){ alert(response.data.name); } function userinfo(username){ $.ajax({ url: "https://api.github.com/users/" + username, jsonpCallback: 'getName', dataType: "jsonp" }); } users = ["torvalds", "twitter", "jquery"] for(var i = 0; i < users.length; i++){ userinfo(users[i]); }
由于jsonp的工作原理,您的请求仅触发了一次。
Jsonp的意思是从外部域向页面添加脚本标记,以避开现代浏览器(以及自2011年4月起的IE6和7)内置的跨站点脚本保护。为了使该脚本与页面上的其余脚本交互,正在加载的脚本需要调用页面上的函数。该函数必须存在于全局名称空间中,这意味着该名称只能有一个函数。换句话说,没有JQuery,单个jsonp请求将如下所示:
<script> function loadJson(json) { // Read the json } </script> <script src="//outsidedomain.com/something.js"></script>
something.js看起来像这样:
loadJson({name:'Joe'})
在这种情况下,something.js具有硬编码的回调以加载其承载的JSON,页面具有硬编码的loadJson函数,以等待诸如此类的脚本加载并调用它。
现在,假设您希望能够从多个来源加载json并告诉每个时间何时结束,甚至多次从同一来源加载JSON,并且能够告诉每个调用何时完成- 即使一个调用被延迟很长时间才完成在以后的通话之后。这种硬编码方法不再起作用了,原因有两个:
something.js的每次加载都调用相同的loadJson()回调-您无法知道哪个请求与哪个回复一起进行。
缓存-一旦加载了something.js,浏览器就不会再向服务器请求它了-它只会从缓存中将其带回,从而破坏您的计划。
您可以通过告诉服务器每次以不同的方式包装JSON来解决这两种情况,而简单的方法是将该信息传递到诸如的querystring参数中?callback=loadJson12345。就像您的页面看起来像这样:
?callback=loadJson12345
<script> function loadJson1(json) { // Read the json } function loadJson2(json) { // Read the json } </script> <script src="//outsidedomain.com/something.js?callback=loadJson1"></script> <script src="//outsidedomain.com/somethingelse.js?callback=loadJson2"></script>
使用JQuery,所有这些都对您来说是抽象的,就像对$ .ajax的正常调用一样,这意味着您期望成功函数会触发。为了确保为每个jsonp加载触发正确的成功函数,JQuery在全局名称空间(如JQuery1233432432432432)中创建了一个长随机回调函数名称,将其作为querystring中的回调参数传递,然后等待脚本加载。如果一切正常,则加载的脚本将调用请求的回调函数JQuery,后者依次从$ .ajax调用中触发成功处理程序。
请注意,“正常工作”要求服务器端读取?callbackquerystring参数并将其包含在响应中,例如?callback=joe-> joe({...。如果它是静态文件,或者服务器无法以这种方式播放,则您可能需要将该文件视为可缓存文件-参见下文。
?callback
?callback=joe
joe({...
如果您希望json进行缓存,则可以通过设置cache:true并将jsonpCallback属性设置为一个硬编码到可缓存json文件中的字符串,来使JQuery做得更接近我的第一个示例。例如,此静态json:
loadJoe({name:'Joe'})
可以像这样在JQuery中加载和缓存:
$.ajax({ url: '//outsidedomain.com/loadjoe.js', dataType: 'jsonp', cache: true, jsonpCallback: 'loadJoe', success: function(json) { ... } });