JSONP不支持循环调用
问题描述
在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);*/ }
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。