0

Suppose I have a web application returning exchange rate for given currency pair at /exchange_rate/ccy1/ccy2, whenever the currency pair is not found in the database, the web app returns 404.

In the front-end JavaScript code, to be fail-safe, whenever local exchange rate query fails, I'll then query a public provider, say currencyconverterapi.com

Let's also assume for some reason (the actual code uses Vue lifecycle hooks), the global variable rate, and the two functions can't be merged and can't be made into async.

The problem with the following code is it only works when local query axios.get('/exchange_rate/EUR/' + ccy) resolves; when it rejects, the amount * rate will execute before axios.get('http://free.currencyconverterapi.com...) sets rate --

var rate;

function getExchangeRate(ccy) {
    return axios.get('/exchange_rate/EUR/' + ccy)
    .then(ret => {
        rate = ret.data.rate;
    }).catch(err => {
        axios.get('http://free.currencyconverterapi.com/api/v5/convert?q=EUR_' 
        + ccy).then(ret => {
            rate = ret.data.results.val;
        });
    });
}

function toEUR(ccy, amount) {
    getExchangeRate(ccy)
    .then(() => {
        return amount * rate;
    });
}

var EURAmount = toEUR('USD', 42);

My question is: is there a way to guarantee rate is properly set by getExchangeRate in toEUR?

5
  • You could check the value of rate in the .then() of toEUR() and if it is not set correctly, do a recursive call of toEUR(), else return. Commented Jun 14, 2018 at 15:54
  • 2
    toEUR() doesn't return your promise. There are a few bad practices in your code, e.g. nesting a promise in another promise, and using a global updated asynchronously. The two functions don't need to be merged, just chained properly with .then(). Commented Jun 14, 2018 at 15:55
  • Have toEUR check if rate===undefined, and return an empty value if so. Assuming toEUR is a Vue computed value, and rate is a data field on the component, it'll automatically run again when rate changes. Commented Jun 14, 2018 at 15:55
  • ...the two functions can't be merged and can't be made into async can't be made into async - then perhaps the return value should't depend on a value that can only be returned via an asynchronous call? The answer to this question is just like all the similar questions - change the way your code works so it's asynchronous, that, and as @PatrickRoberts said, you need to return the Promise from toEUR - return getExchangeRate(ccy)... Commented Jun 14, 2018 at 15:59
  • Thanks everyone for your valuable input. Just wish to clarify more on the seemingly convoluted use of global variable and the two functions for any future visitors -- the snippet here is not a literal copy of the actual code, which uses Vue. "rate" is a Vue data variable, getExchangeRate() is one of the Vue methods, and toEUR() is a beforeMount hook function Commented Jun 15, 2018 at 3:45

1 Answer 1

3

The then in your toEUR function is not waiting for the second request to complete because you are not returning in the catch.

You should also not be using shared state for the rate variable. Just return it as the result of your promise.

function getExchangeRate(ccy) {
    return axios.get('/exchange_rate/EUR/' + ccy)
    .then(ret => ret.data.rate)
    .catch(err => {
        return axios.get('http://free.currencyconverterapi.com/api/v5/convert?q=EUR_' 
        + ccy)
            .then(ret => ret.data.results.val);
    });
}

function toEUR(ccy, amount) {
    return getExchangeRate(ccy)
        .then(rate => amount * rate);
}

toEUR('USD', 42)
    .then(amount => console.log(amount))
    .catch(err => console.error(err));

I would advise factoring out the backup call as a separate function so you don't have nested promises:

function getExchangeRatePublicProvider(ccy) {
    return axios.get('http://free.currencyconverterapi.com/api/v5/convert?q=EUR_' 
        + ccy)
        .then(ret => ret.data.results.val);
}

function getExchangeRate(ccy) {
    return axios.get('/exchange_rate/EUR/' + ccy)
        .then(ret => ret.data.rate)
        .catch(err => getExhangeRatePublicProvider(ccy));
}

function toEUR(ccy, amount) {
    return getExchangeRate(ccy)
        .then(rate => amount * rate);
}

toEUR('USD', 42)
    .then(amount => console.log(amount))
    .catch(err => console.error(err));
Sign up to request clarification or add additional context in comments.

1 Comment

Perfect answer. This covers all the issues I mentioned in my comment and then some.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.