如何进行同步JSONP跨域调用

我遇到了同步跨域调用问题。

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

我之前拨打电话的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); } 

现在,当我们将调用移至不同的域时,我们需要进行跨域调用,我在上面的代码中更改了crossdomain,如下所示:

 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 < length; i++))因为Odata需要在第二个“airportxml”上填充,看起来两个调用都是相互依赖的,所以每当第一次呼叫是同时发送它也进行下一次呼叫。

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

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

正如zi42和jQuery.ajax()文档所解释的那样,’跨域请求和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 }); 

当您使用JSONP时,请求不是通过XHR完成的,而是通过向DOM添加实际的标记。 这就是为什么你不能让它同步。 这根本不可能。

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

使用像BlockUI这样会使屏幕“灰显”的内容,直到你的通话结束。