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

 

posted @ 2015-04-27 11:39  莫名   阅读(2208)  评论(2编辑  收藏  举报