孤灯野火
畅想的天空
posts - 2,comments - 4,trackbacks - 0


问题描述

  在jquery或zepto下,循环调用同一个jsonp

  

复制代码
 for(var i = 0;i<5;i++){
$.ajax({
url:
'https://m.suning.com/authStatus?callback=checkLogin1&_=1430100870770',
dataType:
'jsonp',
jsonpCallback:
'checkLogin1',
success:function(data){
console.info(
'success');
},
error:function(xhr,e){
console.error(e);
}
});
}
复制代码

  结果

  有些成功有些失败了?这是为何?

 

问题解释

  观察jsonp的源码

  

复制代码
 /**
* jsonp请求
* @param options
* @param deferred
* @returns {*}
*/ $.ajaxJSONP = function(options, deferred){
//未设置type,就走 ajax 让参数初始化.
//如直接调用ajaxJSONP,type未设置 if (!('type' in options)) return $.ajax(options)
var _callbackName = options.jsonpCallback, //回调函数名 callbackName = ($.isFunction(_callbackName) ? _callbackName() : _callbackName) || ('jsonp' + (++jsonpID)), //没有回调,赋默认回调 script = document.createElement('script'),
originalCallback
= window[callbackName], //回调函数 responseData,
//中断请求,抛出error事件
//这里不一定能中断script的加载,但在下面阻止回调函数的执行 abort = function(errorType) {
$(script).triggerHandler(
'error', errorType || 'abort')
},
xhr
= { abort: abort }, abortTimeout
//xhr为只读deferred if (deferred) deferred.promise(xhr)
//监听加载完,加载出错事件 $(script).on('load error', function(e, errorType){
//清除超时设置timeout clearTimeout(abortTimeout)
//删除加载用的script。因为已加载完了 $(script).off().remove()
//错误调用error if (e.type == 'error' || !responseData) {
ajaxError(
null, errorType || 'error', xhr, options, deferred)
}
else {
//成功调用success ajaxSuccess(responseData[0], xhr, options, deferred)
}
//回调函数 window[callbackName] = originalCallback
if (responseData && $.isFunction(originalCallback))
originalCallback(responseData[
0])
//清空闭包引用的变量值,不清空,需闭包释放,父函数才能释放。清空,父函数可以直接释放 originalCallback = responseData = undefined
})
if (ajaxBeforeSend(xhr, options) === false) {
abort(
'abort')
return xhr
}
//回调函数设置,给后台执行 window[callbackName] = function(){
/* console.info('callbackName arguments ');
console.info(arguments[0]);
*/ responseData = arguments
/*console.info('responseData ');
console.info(responseData);
*/ } //回调函数追加到请求地址 script.src = options.url.replace(/\?(.+)=\?/, '?$1=' + callbackName)
document.head.appendChild(script)
//超时处理,通过setTimeout延时处理 if (options.timeout > 0) abortTimeout = setTimeout(function(){
abort(
'timeout')
}, options.timeout)
return xhr
}
复制代码

  问题出在多线程处理。 当第一个jsonp刚执行完callback,赋了值时,此时,script的load事件还未触发。第二个JSONP开始初始化。然后第一个script的load开始执行,但它的数据已被清掉了

    第一个jsonp刚执行完callback,响应数据赋给了 responseData 

  

复制代码
//回调函数设置,给后台执行         window[callbackName] = function(){
/* console.info('callbackName arguments ');
console.info(arguments[0]);
*/ responseData = arguments
/*console.info('responseData ');
console.info(responseData);
*/ }
复制代码

 

第二个JSONP开始初始化。没错  responseData又被赋为undefine!!!

  

第一个script的load开始执行,responseData这时判断绝对为undefined,为毛?因为这是闭包,引用最后一个responseData的值。只能进入error了。

 

问题修复

  策略: 

  1, 修改jsonp源码。在执行callback时,将responseData,传入监听函数。诸如function(data){ return function( ...onload... }(responseData);这个太麻烦,而且还得注意开源协议。

  2,规避jsonp的响应。改成这样一种写法。原理是,只用jsonp发请求,然后后台执行window.callback。

复制代码
window.checkLogin1 = function(data){
console.info(
'checkLogin1 success');
console.info(data);
}
for(var i = 0;i<5;i++){
$.ajax({
url:
'https://m.suning.com/authStatus?callback=checkLogin1&_=1430100870770',
dataType:
'jsonp' }); }
复制代码

  切记不能加 jsonpCallback:‘checkLogin1’.原因是,jsonp会重写window[checkLogin1].第二次请求将找不到。

复制代码
//回调函数设置,给后台执行         window[callbackName] = function(){
/* console.info('callbackName arguments ');
console.info(arguments[0]);
*/ responseData = arguments
/*console.info('responseData ');
console.info(responseData);
*/ }
复制代码
posted on 2016-04-14 10:52 孤飞燕 阅读(219) 评论(0)  编辑  收藏 所属分类: JSONP

只有注册用户登录后才能发表评论。


网站导航: