简体   繁体   中英

How can I get this JSONP call to return a value?

The purpose of the following functions are to access a script on Yahoo's servers and lookup a live currency conversion rate which will later be used to process a customer's purchases.

I'm able to access the rates in the JavaScript alert but I can't seem to return them to the Jquery method which originally called the getRate() function.

I've tried a standard return rate; at the end of the parseExchangeRate() function, which doesn't work. I've also tried setting rate as a global variable within parseExchangeRate() but that doesn't work either.

function getRate(from, to) {

    var script = document.createElement('script');

    script.setAttribute('src', "http://query.yahooapis.com/v1/public/yql?q=select%20rate%2Cname%20from%20csv%20where%20url%3D'http%3A%2F%2Fdownload.finance.yahoo.com%2Fd%2Fquotes%3Fs%3D"+from+to+"%253DX%26f%3Dl1n'%20and%20columns%3D'rate%2Cname'&format=json&callback=parseExchangeRate");
    document.body.appendChild(script);

}

function parseExchangeRate(data) {

    var name = data.query.results.row.name;
    rate = parseFloat(data.query.results.row.rate, 10);

    alert("Exchange rate " + name + " is " + rate);

}

$(function() {

    getRate('USD', 'PHP');
    xRatePHP = rate;

    /* Do stuff with rate */

});

Firebug informs me the rate is undefined when I try to access it in Jquery function.

The other thing I tried was setting the last parameter of the http request to callback=rate=parseExchangeRate which (not surprisingly) didn't work either.

UPDATE

@Relfor solved the original problem, which was that rate was not properly declared as a global variable in the global scope. I fixed that and then found a further issue which some have also identified below, which is that after getRate() is called (which can take about 2000ms to update the variable rate ), the script moves on immediately without waiting for rate to be updated and uses rate regardless of whether it's ready.

I tried was attempting to use window.setInterval to create a delay to solve this, when it came to my attention that their was still activity in the thread even though I'd already accepted @Relfor's answer, so I'd prefer to bring it back here so that when we have this working, others can benefit from the solutions.

There is one final (I hope - final!) problem and that is that in order simplify the original question for posting, I ommitted to disclose that I'm actually trying to get two rates from Yahoo! (which possibly a few more planned) and therefore, I'm calling getRate() in a loop as follows:

function getRate(from, to) {

    var script = document.createElement('script');

    script.setAttribute('src', "http://query.yahooapis.com/v1/public/yql?q=select%20rate%2Cname%20from%20csv%20where%20url%3D'http%3A%2F%2Fdownload.finance.yahoo.com%2Fd%2Fquotes%3Fs%3D"+from+to+"%253DX%26f%3Dl1n'%20and%20columns%3D'rate%2Cname'&format=json&callback=parseExchangeRate");
    document.body.appendChild(script);

}

function parseExchangeRate(data) {

    var name = data.query.results.row.name;
    rate = parseFloat(data.query.results.row.rate, 10);

}

var rate = 1.00;
var timer;
var q;
var xRatePHP, xRateGBP;
$(function() {

    function getTheRates() {

        var rateArr = new Array('PHP','GBP');

        for (var x=0; x < rateArr.length; x++) {
            getRate('USD', rateArr[x]);

            q = 0;
            timer = window.setInterval(function(){manageTimer(rateArr[x])},100);

        }
    }

    function manageTimer(c) {

        if (rate != 1) {

            window.clearInterval(timer);

            /* Note that 'c' is undefined according to 'alert' below,
             * so this next line is not working correctly.
             */
            eval("xRate"+c+" = rate;"); 


            alert(c + " = " + rate); // displays 'undefined 43.543'

            rate = 1.00;

        }

        q++;

        if (q > 30 ) {

            window.clearInterval(timer);

            // added below because above isn't working (but neither does this!)
            timer = '';
            alert(c+' timeout'); // 'c' is undefined according to alert ???
            q = 0;

        }

    }

    getTheRates();

    /* Do stuff with the rates */

});

It was suggested that I move /* Do stuff with the rates */ to inside the function parseExchangeRate() but not sure whether that advice would still stand given my revelations about calling getRate() in a loop?

UPDATE 3 (replaced update 2)

I have created a JSbin here: http://jsbin.com/udikas/3/edit with the above which apart from these two issues appears to be working:

1) The timeout mechanism appears not to be working.

2) On line 33 this line alert('start timer (' + x +')'); without which, the timer doesn't seem to start! I have no idea why, but I can't leave that line in.

the variable rate has not been defined

$(function() {

    getRate('USD', 'PHP');
    xRatePHP = rate;

    /* Do stuff with rate */

});

after studying your code it seems that rate is defined in the parseExchange(data) function as

function parseExchangeRate(data) {

    var name = data.query.results.row.name;
    rate = parseFloat(data.query.results.row.rate, 10);

    alert("Exchange rate " + name + " is " + rate);

}

if you want rate to be accessible from the function namespaces without declaring them from within then the rates would have to specified in the global namespace, which is outside any of the functions or loops.

Edit : The namespace problem has been solved and the answer I've given has been accepted, however I would like to add details about the code you are handling here. It has been taken from the gist here: https://gist.github.com/henrik/265014

We are dealing with JSONP here

the reason why parseExchangeRate exists and the context of it can seem mysterious at first glance, albeit its existence is the primary connection between your JSONP request and the data returned by the response.

If you take a closer look at the request:

http://query.yahooapis.com/v1/public/yql?
q=select%20rate%2Cname%20from%20csv%20where%20url%3D'http%3A%2F%2F
download.finance.yahoo.com%2Fd%2F
quotes%3Fs%3DUSDPHP%253DX%26f%3Dl1n'%20and%20
columns%3D'rate%2Cname'&format=json&callback=parseExchangeRate

(I broke the link up into many lines to make it easier to read)
Carefully look at the last segment of the url: callback=parseExchangeRate
This here is the connection, when the JSONP request is complete parseExchangeRate will be called.

So then why did it not work?

Let me display the code once again:

$(function() {

    getRate('USD', 'PHP');
    xRatePHP = rate;

    /* Do stuff with rate */

});

We should break this up:

  • getRate('USD', 'PHP') does the job of loading the JSONP with the respective currency types of 'USD' and 'PHP'
  • xRatePHP = rate assigns the right hand side which is rate to xRatePHP . But this line gave us a problem! our console pal told us that rate is undefined!

The truth: mr.console did not lie, rate is actually undefined, however mr.console without any further commands given by you a few moments later if asked again would reply that rate is actually defined. Is this magic?

What is actually happening is that between the time you go from this line

getRate('USD', 'PHP');

to

xRatePHP = rate;

the JSONP response from mr.yahoo hasn't yet come back, which is why when xRatePHP = rate is issued rate seems to be undefined.

Hard Code Solution
Let us hard code a duration for our code to wait before using rate so that we know that mr.yahoo responded, setTimeout will helps us here:

getRate('USD', 'PHP');
setTimeout(function(){alert(rate)}, 2000);

and everything works now! check the demo at: http://jsbin.com/udikas/1/edit

Soft Code
did you ever consider the case where mr.yahoo takes more than 2000 ms to respond? or maybe even lesser than that? (yahoo is pretty fast!) let us instead take a different approach, this will let us use rate the exact moment we calculate it with parseExchangeRate

for this we will have to add a callback from parseExchangeRate :

function parseExchangeRate(data) {

    var name = data.query.results.row.name;
    rate = parseFloat(data.query.results.row.rate, 10);

    alert("Exchange rate " + name + " is " + rate);

}

to

function parseExchangeRate(data) {

    var name = data.query.results.row.name;
    rate = parseFloat(data.query.results.row.rate, 10);

    alert("Exchange rate " + name + " is " + rate);
    gotTheRate(rate)

}

and then change

$(function() {

    getRate('USD', 'PHP');
    alert(rate)

});

to

function gotTheRate(rate){
    alert(rate);
  }

$(function() {

    getRate('USD', 'PHP');

});

a demo of this can be found at http://jsbin.com/udikas/2/edit

Multiple Callbacks (in response to question update)

Remember hard coding setTimeouts isn't fun, so let us remove that, manageTimer , q , and other such elements from your code, instead we can have:

function getRate(from, to) {

    var script = document.createElement('script');

    script.setAttribute('src', "http://query.yahooapis.com/v1/public/yql?q=select%20rate%2Cname%20from%20csv%20where%20url%3D'http%3A%2F%2Fdownload.finance.yahoo.com%2Fd%2Fquotes%3Fs%3D"+from+to+"%253DX%26f%3Dl1n'%20and%20columns%3D'rate%2Cname'&format=json&callback=parseExchangeRate");
    document.body.appendChild(script);

}

function parseExchangeRate(data) {

    var name = data.query.results.row.name;

    rateDict[name.match(/USD to ([\w]*)/)[1]] = parseFloat(data.query.results.row.rate, 10);
    total_responses++;
  if (total_responses === rateArr.length){
    for (var k in rateDict){
        alert("USD to " + k + " is " + rateDict[k]);
    }
  }
}

var rate = 1.00;
var timer;
var q;
var rateArr = new Array('PHP','GBP')
var total_responses = 0;
var rateDict = {};

$(function() {

    function getTheRates() {


        for (var x=0; x < rateArr.length; x++) {

            getRate('USD', rateArr[x]);

        }
    }

    getTheRates();


}); 

http://jsbin.com/udikas/4/edit

-Relfor

You should re order your code flow:

function getRate(from, to) {
    var script = document.createElement('script');
    script.setAttribute('src', "http://query.yahooapis.com/v1/public/yql?q=select%20rate%2Cname%20from%20csv%20where%20url%3D'http%3A%2F%2Fdownload.finance.yahoo.com%2Fd%2Fquotes%3Fs%3D"+from+to+"%253DX%26f%3Dl1n'%20and%20columns%3D'rate%2Cname'&format=json&callback=parseExchangeRate");
    document.body.appendChild(script);
}
function parseExchangeRate(data) {
    var name = data.query.results.row.name;
    var rate = parseFloat(data.query.results.row.rate, 10);
    alert("Exchange rate " + name + " is " + rate);
    xRatePHP = rate;
    /* Do stuff with rate */
}
$(function() {
    getRate('USD', 'PHP');
    // rate is not yet available here, so don't do anything with it
});

try to put:

var rate = 0;

on top of your code. This will fix the error. Then you have to consider that if you get 0 maybe you are reading the variable at the wrong time, before it gets populated.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM