4

I have an API that return my expected results in a react project.

I wrote this function to call API and get the results:

async function getUsers() {
  const token = store.getState().token;
  const data = { token };
  await MyApi.getUsers(data)
    .then((result) => {
      console.log(result);
      return result;
    })
    .catch((error) => {
      console.log(error);
      return null;
    });
}

const userList = getUsers();

When I console.log the result in the then section of the function, it printed the expected list of users (that is a json array). But when I console.log the userList it is a promise like bellow image:

enter image description here

that means the returned value of getUsers function is a Promise.

1- How can I get the results from this promise?

2- why the PromiseResult is undefined while the result in then section is a json array?

** these codes are not in the react component

the expected result is:

[
 {
  id: 1,
  name: "user1",
  role: "student"
 },
{
  id: 2,
  name: "user2",
  role: "student"
 },
{
  id: 3,
  name: "user3",
  role: "student"
 },
]

and I want to use userList array to create user card:

<div>
{userList ? (
 userList.map((user) =>
 {
  <Card>
    <h1> user.name </h1>
  </Card>
 }
):
(<h1>user list is empty </h1>)
}
Zahra Talebi
  • 675
  • 4
  • 11
  • 26
  • `getUsers` is an `async` function, it implicitly returns a Promise. If you want the resolved value then you should `await` it, or `.then` from it, i.e. `getusers().then(userList => ....)`. – Drew Reese Feb 15 '21 at 20:00
  • Could you please explain how can I assign the `result` of the `getUsers` function to `UserList` ? I'm actually beginner in JS and react – Zahra Talebi Feb 15 '21 at 20:09
  • I thought I already did, based on the limited scope of your snippet. Can you provide a more [Minimal, Complete, and Reproducible Example](https://stackoverflow.com/help/minimal-reproducible-example) for how you are attempting to consume the resolved value? – Drew Reese Feb 15 '21 at 20:14
  • 1
    Does this answer your question? [How do I return the response from an asynchronous call?](https://stackoverflow.com/questions/14220321/how-do-i-return-the-response-from-an-asynchronous-call) – Emile Bergeron Feb 15 '21 at 21:13

3 Answers3

5

Issue

why the PromiseResult is undefined while the result in then section is a json array?

getUsers is an async function, so it implicitly returns a Promise but you don't return any resolved value, it simply awaits the MyApi.getUsers promise to resolve and returns nothing, i.e. a void return, or undefined.

In other words, it looks something like the following, logically:

async function getUsers() {
  const token = store.getState().token;
  const data = { token };
  await MyApi.getUsers(data)
    .then((result) => {
      console.log(result);
      return result;
    })
    .catch((error) => {
      console.log(error);
      return null;
    });
  return undefined;
}

Solution

Return the MyApi.getUsers promise.

async function getUsers() {
  const token = store.getState().token;
  const data = { token };
  return MyApi.getUsers(data)
    .then((result) => {
      console.log(result);
      return result;
    })
    .catch((error) => {
      console.log(error);
      return null;
    });
}

Now in your react code you will likely have some useEffect hook calling this function to set some loaded state.

const [userList, setUserList] = useState([]);

useEffect(() => {
  getusers()
    .then(userList => setUserList(userList))
    .catch(error => {
      // handle any error state, rejected promises, etc..
    });
}, []);

Or

const [userList, setUserList] = useState([]);

useEffect(() => {
  const loadUserList = async () => {
    try {
      const userList = await getusers();
      setUserList(userList)
    } catch(error) {
      // handle any error state, rejected promises, etc..
    }
  };

  loadUserList();
}, []);
Drew Reese
  • 165,259
  • 14
  • 153
  • 181
  • in your last example, is there a particular reason why `loadUserList` is not the entire function passed to `useEffect`? Creating and assigning a function to a variable and then calling it seems unnecessary here.... – Garrett Motzner Feb 16 '21 at 16:16
  • @GarrettMotzner It's completely necessary here. Since React hook callbacks are 100% synchronous, in order to `await` any values we must create an asynchronous function and invoke it, then *that* function can `await` any other asynchronous code. It's a common pattern. Defining it *inside* the effect callback also removes it as a dependency for the effect. In other words `useEffect(async () => { ..... }, [.......]);` isn't valid and doesn't work. – Drew Reese Feb 16 '21 at 16:45
  • But if you changed the last line to `return loadUserList()`, wouldn't you effectively have the same `async` function as `useEffect(async () => { ..... }, [.......]);`? AFAIK, an async function a sync function that returns a promise - so if you don't care about the return value, there is no reason you can't use an async function inplace of a sync function... – Garrett Motzner Feb 16 '21 at 16:55
  • @GarrettMotzner Not quite. The only thing you return from an `useEffect` hook callback is a cleanup function ***to be invoked*** when the component is about to rerender or unmount. Returning the asynchronous `loadUserList` function Promise would cause issue. – Drew Reese Feb 16 '21 at 17:02
  • Got it. I had forgotten that the return value of a hook callback was usually a cleanup callback. Makes sense then, as an async function can't return a sync value, like a cleanup function. React does seem to handle it well, and not cause any errors, but it makes sense to not make that a pattern. – Garrett Motzner Feb 16 '21 at 17:33
1

Why do you get Promise {pending} ? The promise will always log pending as long as its results are not resolved yet. Thus, You must call .then() in order to capture the results. If the function in the .then() handler returns a value, then the Promise resolves with that value to the "userList", however in your case .then() returns another promise, thus the next .then() handler will always contain the value you wish to get.

async function getUsers() {
  const token = store.getState().token;
  const data = { token };
  await MyApi.getUsers(data)
    .then((result) => {
      console.log(result);
      return result;
    })
    .catch((error) => {
      console.log(error);
      return null;
    });
}

const userList =getUsers();
console.log(userList ); // Promise  <pending> 
userList.then((result)=> {
console.log(result) // "Some desired value"
})

another option is just to add await getUsers()

async function getUsers() {
  const token = store.getState().token;
  const data = { token };
  await MyApi.getUsers(data)
    .then((result) => {
      console.log(result);
      return result;
    })
    .catch((error) => {
      console.log(error);
      return null;
    });
}

const userList =await getUsers();
console.log(userList ); // "Some desired value"
yairmea
  • 260
  • 2
  • 9
1

Here is a code snippet it might help you understand it better

//Api response simulation
function fetchUsers() 
{
return Promise.resolve({'name' :'tom'});
}

//async will always return promise
async function getUsers() { 
  const response = await fetchUsers();
  return response;
}

getUsers().then((users) => console.log(users)
 // here you can fill the usersList 
 );

and I think reading this link will help you understand async await deeply https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Asynchronous/Async_await