Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make synchronous JSONP crossdomain call

I got stuck with synchronous crossdomain call.

Earlier in my application we were having domain call, so there were no issues

My Earlier javascript code for making call was as below:

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

Now as we moved our calls to different domain, we need to make crossdomain calls, I changed above code for crossdomain as below:

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

Above Jquery changes are working fine when I put "async:false," in my call, however as per my knowledge we can't have synchronous call in cross domain and if I change to "async:true," the call starts giving error on the line ( for (i = 0, length = oData.length; i < length; i++)) as Odata needs to be filled on the second "airportxml" and it seems both the calls are dependent on each other, so whenever first call is send it simultaneously goes for next call also.

I used ajaxQueue for this also, but no luck.

Please suggest what changes do I need to do.

like image 895
Manoj Singh Avatar asked Jan 17 '23 13:01

Manoj Singh


2 Answers

As explained by zi42, and by the jQuery.ajax() documentation, 'Cross-domain requests and dataType: "jsonp" requests do not support synchronous operation.'

This is not a bad thing, given that a synchronous call tends to result in a poor user experience because it locks up the browser until the response comes back, so I'd steer clear of making a synchronous call even with standard Ajax where it is easy to implement. You want to make two synchronous calls in a row so that would be even worse.

The workaround zi42 suggested in a comment - making a synchronous Ajax call to your own domain and then making the cross-domain call from your server - is the best approach you could take if you really want it to be synchronous.

The other obvious approach is to restructure your code so that it will work asynchronously, by putting the stuff you want to do after each jsonp request into the success callback. That is, within the success callback of the first jsonp request you'd go on to make the second jsonp request. Within the success callback of the second you'd do any final processing. If you need to pass the final results to another part of your code then put the Ajax code in a function that takes a callback as a parameter:

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
});
like image 104
nnnnnn Avatar answered Jan 31 '23 02:01

nnnnnn


When you use JSONP, the request is not done thru XHR, but by adding an actual <script> tag to the DOM. That's why you cannot make it synchronous. It is simply not possible.

like image 20
ziad-saab Avatar answered Jan 31 '23 02:01

ziad-saab