Declare an Async function

Viewed 306

I'm new in React and I'm trying to call an async function, but I get the Unexpected reserved word 'await'. ERROR

So my async function is in a Helper class who is supposed to do all the API call, and I'm calling this Helper class in my Game function

Here is my code from Class Helper :

class Helper {
      constructor() {
        this.state = {
          movieAnswer: {},
          profile_path:"",
          poster_path:"",
          myInit: { method: "GET", mode: "cors" }
        }
      }
  fetchMovieFunction = async (randomMovie) => {
    return new Promise((resolve) => {
    fetch(`${MOVIE_KEY}${randomMovie}?api_key=${API_KEY}`, this.myInit)
    .then(error => resolve({ error }))
    .then(response => resolve({ poster_path: response.poster_path }));
    }); 
  }

and here is where I call fetchMovieFunction in my game function :

const helper = new Helper();


    function Game() {
     useEffect(() => {
        setRandomMovie(Math.floor(Math.random() * (500 - 1) + 1));
        const { poster_path, error } = await helper.fetchMovieFunction(randomMovie)
        if (error)
          return console.log({error});
        setApiMovieResponse({poster_path})
      }, [page]);
   return ();
    }

And I don't understand why on this line const { poster_path, error } = await helper.fetchMovieFunction(randomMovie) I get the Unexpected reserved word 'await'. Error like my function is not declare as an async function

4 Answers

two issues:

  1. You should use await inside an async function

  2. But the callback passed to useEffect should not be async. So, you can create an IIFE and make it async:

    useEffect(() => {
       (async () => {
         setRandomMovie(Math.floor(Math.random() * (500 - 1) + 1));
         const { poster_path, error } = await helper.fetchMovieFunction(randomMovie);
         if (error) return console.log({ error });
         setApiMovieResponse({ poster_path });
       })();
     }, [page]);
    

The callback itself shouldn't be async because that would make the callback return a promise and React expects a non-async function that is typically used to do some cleanup.

You will necessarily need to wrap the hook callback logic into an async function and invoke it. React hook callbacks cannot be asynchronous, but they can call asynchronous functions.

Also, since React state updates are asynchronously processed, you can't set the randomMovie value and expect to use it on the next line in the same callback scope. Split this out into a separate useEffect to set the randomMovie state when the page dependency updates, and use the randomMovie as the dependency for the main effect.

useEffect(() => {
  setRandomMovie(Math.floor(Math.random() * (500 - 1) + 1));
}, [page]);

useEffect(() => {
  const getMovies = async () => { // <-- declare async
    const { poster_path, error } = await helper.fetchMovieFunction(randomMovie);
    if (error) {
      console.log({error});
    } else {
      setApiMovieResponse({poster_path});
    }
  };

  if (randomMovie) {
    getMovies(); // <-- invoke
  }
}, [randomMovie]);

FYI

fetch already returns a Promise, so wrapping it in a Promise is superfluous. Since you are returning the Promise chain there is also nothing to await. You can return the fetch (and Promise chain).

fetchMovieFunction = (randomMovie) => {
  return fetch(`${MOVIE_KEY}${randomMovie}?api_key=${API_KEY}`, this.myInit)
  .then(response => return {
    poster_path: response.poster_path
  })
  .catch(error => return { error });
  
}

In order to do so, It's not suggested to asynchronize a function as a callback function being used in useEffect() since Effect callbacks are synchronous to prevent race conditions.

the better approach would be inside the useEffect callback

check this out:

useEffect(() => {
     async function [name] () {
              .....
              .....
        }
     [name]();

},string[])

fetchMovieFunction doesn't need to be async because you're already returning a promise. Instead just return the fetch (also a promise), and catch any errors from that call in that method:

fetchMovieFunction = (randomMovie) => {
  try {
    return fetch(`${MOVIE_KEY}${randomMovie}?api_key=${API_KEY}`, this.myInit)
  } catch(err) {
    console.log(err);
  }
});

But your useEffect, or rather the function you should be calling within the useEffect, does need to be async (because you're using await, but now you can just pick up the data from the fetch when it resolves.

useEffect(() => {

  async function getData() {
    setRandomMovie(Math.floor(Math.random() * (500 - 1) + 1));
    const { poster_path } = await helper.fetchMovieFunction(randomMovie);
    setApiMovieResponse({ poster_path });
  }

  getData();

}, [page]);
Related