Coding exercise - 5 coins

My idea was this:

let coinIDs = [];
        const limit = 5;

        fetch('https://api.coinpaprika.com/v1/coins')
        .then( response => response.json() )
        .then( coins => {
            for (let i = 0; i < limit; ++i ) {
                const coin = coins[i];
                coinIDs.push( "https://api.coinpaprika.com/v1/tickers/" + coin.id );
            }
        } )
        .then( fetch(coinIDs[0]))

but instead of coinIDs[0] there would be another loop so it would fetch coinIDs[ i ].
If I console.log the coinIDs array, it looks like the links are assembled correctly:

image
However, when I try to fetch, I get this:

image
so apparently I am not passing the link correctly.
I am confused here as the array appears to contain strings, and the fetch() does expect a string…?

I preferred a scalable solution that will only have two API calls, no matter how many coins are queried.

App.js
  getTopIds = async () => {
    const response = await axios.get('https://api.coinpaprika.com/v1/coins');
    return response.data.slice(0, COIN_COUNT).map(coin => coin.id);
  }

  getNewCoinData = async (ids) => {
    let data = [];
    const response = await axios.get('https://api.coinpaprika.com/v1/tickers');
    for (let i = 0; i < response.data.length; i++) {
      for (let j = 0; j < ids.length; j++) {
        if (ids[j] === response.data[i].id) {
          data.push({ 
            key: response.data[i].id,
            name: response.data[i].name,
            ticker: response.data[i].symbol,
            balance: 0,
            price: parseFloat(Number(response.data[i].quotes.USD.price).toFixed(4)),
          });
        }
      }
    }
    return data;
  }

  componentDidMount = async () => {
    const topIds = await this.getTopIds();
    const newCoinData = await this.getNewCoinData(topIds);
    this.setState({coinData: newCoinData});
  }

2 Likes

The parameter inside then is a value and not a function. In promises, you have to pass a callback function to then to make it work. The other problem is that to use a value in the last then, you’ll need to return it in the previous one. Correct code (see a debugger inside to observe the value):

let coinIDs = [];
const limit = 5;

        fetch('https://api.coinpaprika.com/v1/coins')
        .then( response => response.json() )
        .then( coins => {
            for (let i = 0; i < limit; ++i ) {
                const coin = coins[i];
                coinIDs.push( "https://api.coinpaprika.com/v1/tickers/" + coin.id );
            }
            return coinIDs;
        } )
        .then( (coinIDs) => { fetch(coinIDs[0]); })
1 Like

Hi! This took hours :slight_smile: But it works.

componentDidMount = async () => {

    console.log("coinData at the beginning of componentDidMount: ");
    console.log(this.state.coinData);
    let response = await axios.get("https://api.coinpaprika.com/v1/coins");
    let coinId = response.data.slice(0, COIN_COUNT).map(function(coin) {
      return{
        key: coin.id,
      };
    });

    Promise.all([
      axios.get("https://api.coinpaprika.com/v1/tickers/" + coinId[0].key),
      axios.get("https://api.coinpaprika.com/v1/tickers/" + coinId[1].key),
      axios.get("https://api.coinpaprika.com/v1/tickers/" + coinId[2].key),
      axios.get("https://api.coinpaprika.com/v1/tickers/" + coinId[3].key),
      axios.get("https://api.coinpaprika.com/v1/tickers/" + coinId[4].key),
    ]).then(
      (coinResponses) => {
        let coinData = coinResponses.map( response => ({ 
          name: response.data.name, 
          ticker: response.data.symbol, 
          price: response.data.quotes["USD"].price
        }) );
        console.log("coinData in the Promise: ");
        console.log(coinData);
        this.setState({coinData});
      }
    )
  }
1 Like

https://codepen.io/endre-szucs/pen/bGwKpEK?editors=1010

Code(only JS)
document.addEventListener("DOMContentLoaded", () => {
    getCoinsData();
});

let coinIDs = [];
const coinsTOdisplay = 10;

async function getCoinIDs(numberOfCoins) {
    let coinsURLs = [];
    const response = await fetch('https://api.coinpaprika.com/v1/coins');
    const coins = await response.json();
    coins.slice(0, numberOfCoins).map( coin => { coinsURLs.push("https://api.coinpaprika.com/v1/tickers/" + coin.id)});
    return coinsURLs;
}

async function getCoinsData() {
    const divNode = document.querySelector('.js-container');
    let OL = document.createElement('OL');
    
    let promises = [];
    (await getCoinIDs(coinsTOdisplay)).map( url => promises.push(fetch(url)));
    let responses = await Promise.all(promises);
    responses.map( response => {
        response.json()
        .then( coin => {
            let LI = document.createElement('LI');
            LI.innerText = `${coin.name} (${coin.symbol}): ${coin.quotes.USD.price}`;
            OL.appendChild(LI);
        });
        // why is this not working?
        //let coin = await response.json();
    });
    divNode.appendChild(OL);
}

Question:
in function getCoinsData(), why is this working:

responses.map( response => {
        response.json()
        .then( coin => {
....

and this isn’t working:

responses.map( response => {
      let coin = await response.json();

error message:

Uncaught SyntaxError: await is only valid in async function

in the working code, it can be seen that it is in fact an async function…

The former is working, because response.json() returns a promise, which has a .then method.

The latter is not working, because the await operator can only be used in an async function. You have to explicitly declare the function as async, otherwise await is not working. See the useEffect lesson for an example in the functional components section.

1 Like

Thank you so much!
When doing the useEffect lesson, I told myself to remember to declare function as async if I use await inside it, but looks like I forgot quite soon:)

1 Like

Took me over an hour,
only to find out why it didn’t work earlier
because I did not add axios to my codepen lol :rofl: hahaha

2 Likes

https://codepen.io/kostka-tech/pen/yLarNBd

1 Like

Implemented the promise.all method into app.js file. See code snippet below.

Snippet App.js
componentDidMount = async() => {
    // Retrieve ticker from coinpaprika
    let response = await axios.get('https://api.coinpaprika.com/v1/coins')
    let coinData = response.data.slice(0, COIN_COUNT).map(function(coin){
      return {
        key: coin.id,
        name: coin.name,
        ticker: coin.symbol,
        balance: 0,
        price: 0
      };
    });

    // Retrieve prices from coinpaprika
    const textAPI = 'https://api.coinpaprika.com/v1/tickers/';
    let promises = coinData.map(coin => {
      return axios.get(textAPI.concat(coin.key));
    });

    let responses = await Promise.all(promises);
    responses.map(function(coin, index){
      coinData[index].price = coin.data.quotes.USD.price;
    });

    // Set state
    this.setState({coinData});
  }; 

3 Likes

here is the solution I use Atom to check
html file

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
  <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<title>Coins</title>
</head>
<body>
   <div class = "js-container"> </div>
     <script type="text/javascript" src=".\coinlist.js"></script>
  </body>
<html>

js file

var node = document.querySelector('.js-container');
var text = [];

Promise.all([
axios.get('https://api.coinpaprika.com/v1/tickers/btc-bitcoin'),
axios.get('https://api.coinpaprika.com/v1/tickers/eth-ethereum'),
axios.get('https://api.coinpaprika.com/v1/tickers/usdt-tether'),
axios.get('https://api.coinpaprika.com/v1/tickers/dot-polkadot'),
axios.get('https://api.coinpaprika.com/v1/tickers/xrp-xrp')
]).then(
(coinResponses) => {
  const coins = coinResponses
  .map(response=>
  `<li>${response.data.name} (${response.data.symbol}):
   ${response.data.quotes ['USD'].price}</li>`)
  .join('');
   node.innerHTML = `<ol>${coins}</ol>`;
}
);

3 Likes

Github Link

I tried it first on my own, using for loops, but I found it fiddly to attach ticker names to the URLs.

I then looked at some of the submitted answers here on the forum and I found a very nice answer by @marsrvr which was exactly what I was looking to accomplish. I forgot about maps :neutral_face: But I’m very familiar with the concept - I have a maths background - so I implemented the exact same solution with some syntax change.

I knew that the solution I was going to come up with would be scalable, compared to extracting data from the 5 URLs you copy and paste into the .js file, which is why I wanted to spend a little extra time to present a more concrete solution.

BONUS: my failed (and unfinished) code
  let response = await axios.get('https://api.coinpaprika.com/v1/coins')
    let coinDataUrl = 'https://api.coinpaprika.com/v1/tickers/';
    let coinDataUrlList = [];
    let coinData = response.data.slice(0, COIN_COUNT).map(function(coin) {
      return {
        key: coin.id,
        name: coin.name,
        ticker: coin.symbol,
      }
    });
    console.log(coinData);
    
    for (i = 0; i < COIN_COUNT; i++) {
      coinDataUrlNew = coinDataUrl;
      coinDataUrlNew = coinData[i].ticker + '-' + coinData[i].name;
      coinDataUrlList[i] = coinDataUrlNew;
    }
    console.log(coinDataUrlList);
    
    for (i = 0; i < COIN_COUNT; i++) {
      response[i] = await axios.get
    }

EDIT: See the “batched/scalable data fetch” commit for the exact changes at the time

3 Likes

Keep up the good work ! Taking inspiration from other people is a faster way of learning. And you’re going on the right path. Good luck! :smile:

2 Likes
componentDidMount = async () => {
  let coinResponses = await Promise.all([
    axios.get('https://api.coinpaprika.com/v1/coins'),
    axios.get('https://api.coinpaprika.com/v1/tickers')
    
  ]);
 let coinData = coinResponses[0].data.slice(0,COIN_COUNT).map(function(coin){
    return {
      key: coin.id,
      name: coin.name,
      ticker: coin.symbol,
      balance: 0,
      price: insertPrice(coinResponses[1],coin.id)   
   };
 });

 this.setState({coinData});  
 }
function insertPrice(data, key){
   let val = data.data.find(x => x.id == key);  
   let price = val.quotes['USD'].price;
  return price;
  
}
2 Likes
2 Likes

https://codepen.io/jellosss/pen/rNWmxgM?editors=1010

somehow this code wouldn’t run properly without the axios script in the html file. But i didn’t saw anybody else use it in the html file can anyone maybe explain a bit ?

https://tq0jc.csb.app/

First time on codepen as creator, but worked!

See the Pen oNYWYyQ by evele (@evele-the-decoder) on CodePen.

1 Like

in the video is explained. Go to Settings->JS->Add External Scripts/Pens and search for axios

3 Likes

https://codepen.io/derdis14/pen/NWbwwmG?editors=1010

2 Likes