1

I've looked at several questions on using async/await with a forEach loop but nothing seems to cover my user case... How can I get the code below to work? Right now I'm getting the following error:

await is a reserved word

Here's the code:

export const fetchUserBookmarks = ( bookmarksIDs ) => async ( dispatch, getState, api ) => {

    dispatch({
        type: 'IS_FETCHING_BOOKMARKS'
    });

    try {

        bookmarks = [];

        bookmarksIDs.forEach( bookmarkID => {
            const bookmark = await api.get( selectedPostByIdEP + bookmarkID ); 
            bookmarks.push( bookmark ); 
        }); 

        dispatch({
            type: 'HAS_FETCHED_BOOKMARKS', 
            payload: bookmarks
        });

    } catch( error ) {

        dispatch({
            type: 'FAILED_FETCHING_BOOKMARKS', 
            payload: error
        });

    }

}
grazdev
  • 1,082
  • 2
  • 15
  • 37
  • add `async` to forEach loop, `bookmarksIDs.forEach(async bookmarkID => {...})` – Medet Tleukabiluly Apr 09 '18 at 06:56
  • 3
    sure, that would work, but not as expected, for example, `dispatch` will be called before anything is pushed to bookmarks regardless of what you do inside forEach - see [this answer](https://stackoverflow.com/questions/37576685/using-async-await-with-a-foreach-loop) – Jaromanda X Apr 09 '18 at 06:56

4 Answers4

8

First, To use await you should declare the function as async. You have done so with the outer function but not with the inner function.

The change will look something like this:

bookmarksIDs.forEach(async bookmarkID => {

Second, what you probably want is to run those api calls in parallel.

You can replace forEach with a map call and await all the resulting promises together.

To do that your code should look something like this:

const bookmarks = await Promise.all(
  bookmarksIDs.map(bookmarkID => 
    api.get( selectedPostByIdEP + bookmarkID )
  )
); 

--

It seems that if bookmarks is not declared anywhere else it causes a problem. using const or let should solve that problem.

Tal Z
  • 3,170
  • 17
  • 30
  • How does the first step relate to the second step? – Ry- Apr 09 '18 at 07:06
  • The first point is to show what's missing in your code. The second point is to show how to modify the code to run in parallel, if that is what you intended to do. – Tal Z Apr 09 '18 at 07:08
  • But `async` isn’t missing. Putting it there wouldn’t work at all. – Ry- Apr 09 '18 at 07:12
  • You can't use `await api.get` in the inner without it. But I agree, that is why I prefer the solution in the second part of my answer. – Tal Z Apr 09 '18 at 07:13
  • @TalZ OP here. I've just tried your code (second part) but it doesn't seem to work for me. More precisely, the requests are being made and are successful (I can see that in the inspector's Network tab), but in fact any code that follows the `Promise.all` snippet doesn't execute. Even just a `console.log` prints nothing to the console... – grazdev Apr 09 '18 at 07:49
  • Nope, the requests are being made and are successful (I can see that in the inspector's Network tab). – grazdev Apr 09 '18 at 07:53
  • prints nothing or doesn't reach the `console.log`? – Tal Z Apr 09 '18 at 07:55
  • Doesn't reach it. But I think I've just fixed it: adding a `const` before `bookmarks` made it work! I will update your answer. – grazdev Apr 09 '18 at 08:00
1

forEach isn’t a loop; it’s a function that you pass a function to. There’s no way to get a promise out of it. If you want to perform api.gets one by one, you can use a for loop:

for (const bookmarkID of bookmarksIDs) {
    const bookmark = await api.get(selectedPostByIdEP + bookmarkID); 
    bookmarks.push(bookmark); 
}

and if you want to do them in parallel, you can create several promises and use Promise.all to collect their results:

const bookmarks = await Promise.all(
    bookmarksIDs.map(
        bookmarkID => api.get(selectedPostByIdEP + bookmarkID)
    )
);
Ry-
  • 218,210
  • 55
  • 464
  • 476
1

forEach loop is not compatible with promise and async functions. Use for.. of loop instead. It would work fine.

async(data)=>{
    const ids = ["xyz","abc"]
    for (const id of ids){
    let data = await Collection.findById(id)
    console.log(data)
    } 
}
0

You'll want to do something like this instead

try {

    bookmarks = await Promise.all(bookmarksIDs.map( bookmarkID => api.get( selectedPostByIdEP + bookmarkID )); 

    dispatch({
        type: 'HAS_FETCHED_BOOKMARKS', 
        payload: bookmarks
    });

}
Jaromanda X
  • 53,868
  • 5
  • 73
  • 87