0

Caveat: I'm learning to work with JavaScript Promises and API calls, so I try to write the code in promise.then format as well as async / await, so I know how to use both.

I have a container that I am initially filling with images from 15 successive API calls; each call returns a single image. As scrolling happens, more API calls are made. I am able to make everything work with straightforward Promises as well as async / await.

I am trying to rewrite the API calls to make them concurrent rather than successive. My new code doesn't work - depending on what I've done, I get no result (and no error in my console) or a typeError of undefined. I don't know what I am doing wrong.

This is the code that is working - in both formats (not including the page load code and intersectionObserver)

promises only

const getImage = function () {
  fetch("https://api.api-ninjas.com/v1/randomimage", {
    method: "GET",
    headers: {
      "X-API-Key": "###",
      Accept: "image/jpg", 
    },
  })
    .then((response) => {
      if (!response.ok) throw new Error("Network failure"); 
      return response.blob();
    })
    .then((blob) => {
      const img = document.createElement("img");
      img.src = URL.createObjectURL(blob);
      container.append(img);
    })
    .catch((err) => console.error(`${err.message}`));
};

async / await

const getImage = async function () {
  try {
    const response = await fetch("https://api.api-ninjas.com/v1/randomimage/", {
      method: "GET",
      headers: {
        "X-API-Key": "###",
        Accept: "image/jpg", 
      },
    });
    if (!response.ok)
      throw new Error(`Network failure! Status: ${response.status}`); 

    const data = await response.blob();
    const img = document.createElement("img");
    img.src = URL.createObjectURL(data);
    container.append(img);
  } catch {
    (err) => console.error(`${err.message}`);
  }
};

This is what I am trying for concurrent calls; I am calling the function after disabling all my other page load code just to see if it's working at all. Nothing loads on my page and I get no error or TypeError in my console.

const getImage = function () {
  fetch("https://api.api-ninjas.com/v1/randomimage", {
    method: "GET",
    headers: {
      "X-API-Key": "###",
      Accept: "image/jpg", 
    },
  });
};

const firstImages = async function () {
  try {
    const res = await Promise.all([
      getImage(),
      getImage(),
      getImage(),
      getImage(),
      getImage(),
      getImage(),
      getImage(),
      getImage(),
      getImage(),
      getImage(),
      getImage(),
      getImage(),
      getImage(),
      getImage(),
      getImage(),
    ]);
    const data = await Promise.all(res.forEach((r) => r.blob()));
    console.log(data);
    data.forEach((d) => {
      const img = document.createElement("img");
      img.src = URL.createObjectURL(d);
      container.append(img);
    });
  } catch {
    (err) => console.error(`${err.message}`);
  }
};

firstImages();
RK3110
  • 15
  • 4
  • 1
    You're not returning the `fetch` from the `getImage` function. And `await Promise.all(res.forEach((r)` won't work because `forEach` doesn't return an array - it operates in place. You probably want `map`. – Andy Jun 29 '23 at 09:07
  • Also, `.catch((err) => console.error(``${err.message}``));` Avoid doing that, your eating the exceptions, so the caller will think things were A ' OK. Just leave out, your console will give you better error messages to boot. – Keith Jun 29 '23 at 09:11
  • If you want implict return use arrow function – Simone Rossaini Jun 29 '23 at 09:15
  • Thank you everyone. I appreciate the guidance. – RK3110 Jun 29 '23 at 11:04

1 Answers1

0

Let me try to help you out a bit. Also added a small loop so you can decide how many images you actually want to bring out. See comments in code as well:

const getImage = function () {
  // Firstly, as Andy wrote in comments, return the fetch
  return fetch("https://api.api-ninjas.com/v1/randomimage", {
    method: "GET",
    headers: {
      "X-API-Key": "###",
      Accept: "image/jpg", 
    },
  });
};

const firstImages = async function (numberOfImages = 1) {
  try {
    const fetchArr = [];

    let i = 0;
    while (i <= numberOfImages) {
      fetchArr.push(getImage());
      i++;
    }

    const res = await Promise.all(fetchArr);

    // As was pointed out by another comment, use map here, as it will return an array that the Promise.all can iterate over.
    const data = await Promise.all(res.map((r) => r.blob()));

    data.forEach((d) => {
      const img = document.createElement("img");
      img.src = URL.createObjectURL(d);
      container.append(img);
    });
  } catch {
    // Probably best (as Keith commented) to just pass the error object to the console.error. It will print a few extra things that are normally noteworthy when tragedy strikes
    (err) => console.error(err);
  }
};

firstImages(15);

Good luck!!

Lior Kupers
  • 528
  • 6
  • 18