【问题标题】:How to make synchronous JSONP crossdomain call如何进行同步 JSONP 跨域调用
【发布时间】:2012-06-02 14:10:33
【问题描述】:

我被同步跨域调用卡住了。

在我的应用程序的早期,我们进行了域调用,所以没有问题

我之前拨打电话的javascript代码如下:

function EKXMLProvider(oDropdown, sDefault, sXML, sFilterUrl, fireRequestOnce)
{
    var oXMLHTTP, i, length, oData, sValue, sDisplay, sName, sMatch, oRegExp;

    if (!oDropdown)
        return;

    // XMLHTTP Object to retrieve the xml document
    oXMLHTTP = this.createXMLHttpRequest();
    this.FilterUrl = sFilterUrl;
    if (sFilterUrl != previousFilterUrl){
        oXMLHTTP.open("GET", sFilterUrl, false);
        oXMLHTTP.send(null);
        sFilterData = oXMLHTTP.responseText
        previousFilterUrl = sFilterUrl;
    }
    if(!fireRequestOnce ||(fireRequestOnce && retrievedData == null))
    {
        this.documentUrl = sXML;
        oXMLHTTP.open("GET", this.documentUrl, false);
        oXMLHTTP.send(null);

        oData = oXMLHTTP.responseXML.documentElement.childNodes;

        if(fireRequestOnce)
            retrievedData = oData;
    }
    else if(retrievedData != null)
    {
        oData = retrievedData;
    }
    this.suggestData = new Array();

    // Filter out all 2 and 3 letter codes (airport, city, country)
    oRegExp = new RegExp("\\s+\\(\\w{2,3}\\)", "gi");
    var iCount = 0    
    for (i = 0, length = oData.length; i < length; i++)
    {
        sValue = oData[i].attributes.getNamedItem("v").value;
        sDisplay = oData[i].attributes.getNamedItem("d").value;
        sName = oData[i].attributes.getNamedItem("n").value;
        //sMatch = oData[i].attributes.getNamedItem("m").value;
        sMatch = oData[i].attributes.getNamedItem("e").value;

        if (sFilterData.search(sValue) != -1){
            this.suggestData[iCount] = new EKSuggestData(sName + " (" + sValue + ")", sDisplay, sValue, sMatch, sMatch.replace(oRegExp, ""));
            iCount++;
        }
    }

    // Call the inherited class
    EKSuggestProvider.call(this, oDropdown, sDefault);
}

现在我们将调用转移到不同的域,我们需要进行跨域调用,我将上面的跨域代码更改如下:

function EKXMLProvider(oDropdown, sDefault, sXML, sFilterUrl, fireRequestOnce)
{
    var oXMLHTTP, i, length, oData, sValue, sDisplay, sName, sMatch, oRegExp;
    var qr = "&jsonpcall=true";
    if (!oDropdown)
        return;

    // XMLHTTP Object to retrieve the xml document
    oXMLHTTP = this.createXMLHttpRequest();

    this.FilterUrl = sFilterUrl;

    if (sFilterUrl != previousFilterUrl){
    //alert(sFilterUrl);
        //oXMLHTTP.open("GET", sFilterUrl, false);
        //oXMLHTTP.send(null);
        //sFilterData = oXMLHTTP.responseText

        // queue up an ajax request
        $.ajax({
        url: sFilterUrl + qr,
        type: "GET",
        cache: true,
        async:false,
        contentType: "application/javascript; charset=utf-8",
        dataType: "jsonp",
        jsonpCallback: "airport", 
        success: function(data, textStatus, jqXHR) 
        {               
            if (data.airport[0] != '')
            {
                    sFilterData = data.airport[0];
            } 
        }
        });

        previousFilterUrl = sFilterUrl;        
    }

    if(!fireRequestOnce ||(fireRequestOnce && retrievedData == null))
    {
        //alert(sXML);
        this.documentUrl = sXML;
        //oXMLHTTP.open("GET", this.documentUrl, false);
        //oXMLHTTP.send(null);

        // queue up an ajax request
          $.ajax({
            url: sXML + qr,
            type: "GET",
            async:false,
            cache: true,
            contentType: "application/javascript; charset=utf-8",
            dataType: "jsonp",
            jsonpCallback: "airportxml", 
            success: function(data, textStatus, jqXHR) 
            {                 
                  var xmlDoc = $.parseXML(data.myresult);
                oData = xmlDoc.documentElement.childNodes; 
                alert(oData);
            }
            });

        //oData = oXMLHTTP.responseXML.documentElement.childNodes;

         if(fireRequestOnce)
             retrievedData = oData;
    }
    else if(retrievedData != null)
    {
        oData = retrievedData;
    }
    this.suggestData = new Array();

      // Filter out all 2 and 3 letter codes (airport, city, country)
      oRegExp = new RegExp("\\s+\\(\\w{2,3}\\)", "gi");
      var iCount = 0    
    for (i = 0, length = oData.length; i < length; i++)
    {
        sValue = oData[i].attributes.getNamedItem("v").value;
        sDisplay = oData[i].attributes.getNamedItem("d").value;
        sName = oData[i].attributes.getNamedItem("n").value;
        //sMatch = oData[i].attributes.getNamedItem("m").value;
        sMatch = oData[i].attributes.getNamedItem("e").value;

          if (sFilterData.search(sValue) != -1){
            this.suggestData[iCount] = new EKSuggestData(sName + " (" + sValue + ")", sDisplay, sValue, sMatch, sMatch.replace(oRegExp, ""));
            iCount++;
        }
    }

    // Call the inherited class
    EKSuggestProvider.call(this, oDropdown, sDefault);
}

当我在调用中添加“async:false”时,上述 Jquery 更改工作正常,但是据我所知,我们不能在跨域中进行同步调用,如果我更改为“async:true”,调用开始在行 ( for (i = 0, length = oData.length; i &lt; length; i++)) 上给出错误,因为 Odata 需要在第二个“airportxml”上填充,而且似乎两个调用都相互依赖,所以每当第一个调用发送时,它同时进行下一个调用。

我也为此使用了 ajaxQueue,但没有运气。

请建议我需要做哪些更改。

【问题讨论】:

  • 你可以将该代码移动到回调中。你的构造函数做了太多的实际工作,它应该只是构造和初始化一个对象
  • @Esailija 能否请您进一步清除它,任何示例链接或代码

标签: jquery cross-domain


【解决方案1】:

正如 zi42 和 jQuery.ajax() documentation 所解释的,“跨域请求和 dataType: "jsonp" 请求不支持同步操作。”

这不是一件坏事,因为同步调用往往会导致糟糕的用户体验,因为它会锁定浏览器直到响应返回,所以即使使用标准 Ajax,我也会避免进行同步调用容易实现的地方。您想连续进行两次同步调用,这样会更糟。

评论中建议的解决方法 zi42 - 对您自己的域进行同步 Ajax 调用,然后从您的服务器进行跨域调用 - 如果您真的希望它是同步的,您可以采用的最佳方法。

另一种明显的方法是重组你的代码,使其能够异步工作,方法是将你想要在每个 jsonp 请求之后执行的操作放入成功回调中。也就是说,在第一个 jsonp 请求的成功回调中,您将继续发出第二个 jsonp 请求。在第二个成功回调中,您将进行任何最终处理。如果您需要将最终结果传递给代码的另一部分,则将 Ajax 代码放入以回调为参数的函数中:

function doMyAjaxCalls(callbackFunc) {
   // make first request
   $.ajax({
      ...
      dataType: "jsonp",
      success: function(data, textStatus, jqXHR) {
         // do something with first response, then
         // make second request              
         $.ajax({
            ...
            dataType: "jsonp",
            success: function(data, textStatus, jqXHR) {
               // do something with second response, then
               // do final processing, then
               callbackFunc(dataHere);
            }
         });
      }
   });
}

doMyAjaxCalls(function(response) {
   // do something with response
});

【讨论】:

  • 两个 ajax 调用的 URL 是什么?它会是相同的网址吗?如果是,在这种情况下不会两次调用相同的数据吗?
  • @sarojanand - URL 可以是任何东西。 OP 有两个不同的 URL。
【解决方案2】:

当你使用 JSONP 时,请求不是通过 XHR 完成的,而是通过向 DOM 添加一个实际的 &lt;script&gt; 标签来完成的。这就是为什么你不能让它同步。这根本不可能。

【讨论】:

  • 使用 jquery 或任何东西实现上述内容的其他选项是什么
  • 唯一的选择是在同一个域上有一个“代理”脚本,并将你想要检索的 URL 传递给它。假设您的 javascript 在 domain1.com 上,并且您想向 http://domain2.com/some_resource 发出 ajax 请求,那么您将向 http://domain1.com/domain2_proxy.php 发出 ajax 请求。在 domain2_proxy.php 中,您将向实际 URL 发出 cURL 请求,并传递在 $_GET 中传递给您的任何参数。然后你会返回响应表单 cURL。
  • @zi42,我不能有代理调用类型的实现,好像我又要调用domain1 跨域直接调用有什么用,我想要类似上面的实现...请建议因为这对我来说真的很痛苦
【解决方案3】:

另一种选择是阻止 UI(不阻止执行)。

使用BlockUI 之类的东西会使屏幕“变灰”,直到您的通话结束。

【讨论】:

    【解决方案4】:

    我是新的 JQuery 和 Ajax。当我遇到类似问题时,一个对我有用的选项是将数据类型从 JSONP 转换为 text 和 async:false。现在您只需相应地解析文本中的响应。

    $.ajax({
            url: url,
            timeout: 600000,
            dataType: "text",
            async:false,
            success: function (data) {
                console.log(data);
            },
            error: function (xhr, textStatus, errorThrown) {
                console.log('failed to download);
                console.log("error: " + errorThrown);
            }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-01-11
      • 1970-01-01
      • 2013-07-28
      • 1970-01-01
      • 2012-12-22
      • 2012-05-28
      • 2013-10-15
      • 1970-01-01
      相关资源
      最近更新 更多