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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM