1

I have a certain promise chain in my code that looks like this:

  myPromise()
    .then(getStuffFromDb)
    .then(manipulateResultSet)
    .then(manipulateWithAsync)
    .then(returnStuffToCaller)

Now, in my manipulateWithAsync I'm trying to enhance my result set by calling the DB again, but it's not working as I expected, since while debugging i figured out the control moves to the next function which is the returnStuffToCaller

here's an idea of what's into my manipulateWithAsync function:

function manipulateWithAsync(rs) {
  return rs.map( async function whoCares(singleRecord) {
      let smthUseful = await getMoreData(singleRecord.someField);
      singleRecord.enhancedField = smthUseful;
      return singleRecord;
  })
}

I get the point of this behaviour: the map function does work as expected and the promise chain doesn't give a duck about it since it's not working with the awaits. Is there a way to allow my returnStuffToCaller function to wait till the async function did his job?

I also use bluebird and i tried to use coo-routine, so if you thing that it's a good solution I'll post my bluebird coo-routine failing code :)

Thanks!

4
  • 6
    return Promise.all(rs.map(...)) but it might help to return something from your map function, otherwise you're going to resolve with an array filled with undefined Commented Jul 3, 2018 at 9:13
  • yeah man, i clearly return something in my map. i wrote the code on the fly while creating the post, thanks for pointing out, gonna edit right away Commented Jul 3, 2018 at 9:19
  • But as @PatrickRoberts pointed out, you need to have Promise.all, otherwise the promise resolves with the array returned by manipulateWithAsync. Commented Jul 3, 2018 at 9:22
  • 1
    Confirmed, just modified my code and it works properly. @Patrick if you can create an answer i'd be happy to approve it. Commented Jul 3, 2018 at 9:23

3 Answers 3

2

The problem is in using async/await with Array.map

This answer should help: https://stackoverflow.com/a/40140562/5783272

Sign up to request clarification or add additional context in comments.

2 Comments

yep, my question is actually a duplicate
In this case Promise.map(arr, fn) can be used
1

rs.map iterator jumps to the next element without waiting in each separate iteration. You need something like asyncMap You can use - https://github.com/caolan/async or either implement yourself

async function asyncMap(array, cb) {
  for (let index = 0; index < array.length; index++) {
      return await cb(array[index], index, array);
  }
}

*cb function must be async one

1 Comment

This one works too, I think an easier change would be to use forEach over map. Thanks!
1

Wrap your map with Promise.all return the Promise then await for the results wherever you call the manipulateWithAsync.

// MOCKS FOR DEMO
// Test data used as input for manipulateWithAsync
const testData = [
  { recordNumber: 1 },
  { recordNumber: 2 },
  { recordNumber: 3 }
];

// Mock function which returns Promises which resolve after random delay ranging from 1 - 3 seconds
const getMoreData = () =>
  new Promise(resolve => {
    const calledAt = Date.now();
    setTimeout(() => {
      resolve({
        usefulData: `Promise called at ${calledAt}`
      });
    }, Math.floor(Math.random() * 3000) + 1000);
  });

// SOLUTION / ANSWER
const manipulateWithAsync = async rs =>
  Promise.all(
    rs.map(async singleRecord => {
      const smthUseful = await getMoreData(singleRecord.someField);

      // Instead of manipulating original data,
      // which might cause some unwanted side effects going forward,
      // instead return new objects
      return { ...singleRecord, enhancedField: smthUseful };
    })
  );

await manipulateWithAsync(testData);

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.