For loop with fetch returning empty array

I’m writing a server route that makes api calls.

I need to make two different fetch requests cause I need more info that’s coming in the first fetch.

The problem is that I’m declaring a variable out of the promise scope and for some reason, my res.send is not awaiting until the array gets full.

I need to iterate until result 9 (I can’t use theDogApi’s predefined filters to show nine results!)

if (req.query.name) {
    var myRes = [];
    fetch(`https://api.thedogapi.com/v1/breeds/search?name=${req.query.name}&apikey=${key}`)
        .then(r => r.json())
        .then( data => {

            for (let i = 0; i < 8 && i < data.length; i++) {
                fetch(`https://api.thedogapi.com/v1/images/${data[i].reference_image_id
                    }`)
                    .then(r => r.json())
                    .then(datos => {

                        myRes.push({ ...data[i], ...datos });
                    })
            }
        })
        .then(res.send(myRes))
}

I’ll appreciate the help!

30 thoughts on “For loop with fetch returning empty array”

  1. Here is an example of an async function unsing await:

    async function fun(queryName, key){
      const r = [];
      let firstWait = await fetch(`https://api.thedogapi.com/v1/breeds/search?name=${req.query.name}&apikey=${key}`);
      let firstJson = await firstWait.json(); // must be an Array
      for(let i=0,n=8,a,j,l=firstJson.length; i<n && i<l; i++){
        a = await fetch('https://api.thedogapi.com/v1/images/'+firstJson[i].reference_image_id);
        j = await a.json(); r.push(j);
      }
      return r;
    }
    // assumes req, req.query, req.query.name, and key are already defined
    fun(req.query.name, key).then(a=>{
      // a is your JSON Array
    });
    Reply
  2. You can try using Promise.all to turn your array of fetch calls into an aggregate promise that resolves to an array of responses when all have arrived. If any fail, the whole thing fails (use Promise.allSettled if you don’t want all-or-nothing semantics). Don’t forget to catch the error.

    Although the code doesn’t show it, be sure to check response.ok to make sure the request actually succeeded before calling .json(). Throwing an error if !repsonse.ok and handling it in the .catch block is a typical strategy. Writing a wrapper on fetch is not a bad idea to avoid verbosity.

    Lastly, note that Array#slice replaces the for loop. For arrays with fewer than 8 elements, it’ll slice as many as are available without issue.

    // mock everything
    const fetch = (() => {
      const responses = [
        {
          json: async () => 
            [...Array(10)].map((e, i) => ({reference_image_id: i}))
        },
        ...Array(10)
          .fill()
          .map((_, i) => ({json: async () => i})),
      ];
      return async () => responses.shift();
    })();
    const req = {query: {name: "doberman"}};
    const key = "foobar";
    const res = {send: response => console.log(`sent ${response}`)};
    // end mocks
    
    fetch(`https://api.thedogapi.com/v1/breeds/search?name=${req.query.name}&apikey=${key}`)
      .then(response => response.json())
      .then(data => 
        Promise.all(data.slice(0, 8).map(e =>
          fetch(`https://api.thedogapi.com/v1/images/${e.reference_image_id}`)
            .then(response => response.json())
        ))
      )
      .then(results => res.send(results))
      .catch(err => console.error(err))
    ;
    Reply

Leave a Comment