11

Consider the following code which print messages to console after I/O operations complete, in theory.

const foo = (num) => new Promise(resolve => setTimeout(resolve, num * 1000)); // An async I/O function in actual code
array = [[1, 2, 3], [1, 2, 3] , [1, 2, 3]];

const promiseArray = array.map(arr => {
  arr.map(num => {
    return (async () => {
      await foo(num);
      console.log(num);
    });
  });
}).flat();

await Promise.all(promiseArray);

I don't know why but it doesn't work. Nothing was printed to the console.


However it would work if I wrap the async function within a Promise constructor

const foo = (num) => new Promise(resolve => setTimeout(resolve, num * 1000)); // An async I/O function in actual code
array = [[1, 2, 3], [1, 2, 3] , [1, 2, 3]];

const promiseArray = array.map(arr => {
  arr.map(num => {
    return new Promise(async () => {
      await foo(num);
      console.log(num);
    });
  });
}).flat();

await Promise.all(promiseArray);

How should I rewrite the code to get rid of the Promise constructor?

1
  • 2
    Two problems: 1. in the first callback you don't return anything, so promiseArray is just [undefined, undefined, undefined]. 2. Even if you put a return, you aren't returning promises but an array of async functions. You have to execute them to get promises. Commented Jul 26, 2020 at 16:24

4 Answers 4

11

Promise.all takes an array of promises as its argument, not an array of async functions. Also you were missing a return statement. You should write

const promiseArray = array.flatMap(arr => {
  return arr.map(async num => {
    await foo(num);
    console.log(num);
  });
});

await Promise.all(promiseArray);

or

const promiseArray = array.map(async arr => {
  await Promise.all(arr.map(async num => {
    await foo(num);
    console.log(num);
  }));
});

await Promise.all(promiseArray);
Sign up to request clarification or add additional context in comments.

4 Comments

I am a little bit confused. Doesn't the following expression create an async function instead of a Promise? async num => { await foo(num); console.log(num); }
@Max Yes, and map calls that async function, mapping the arr to an array of promises.
Can I see it as the following statement? arr.map((i) => (async num => { await foo(num); console.log(num); })(i))
@Max Yes, but using an IIFE there is rather pointless.
4

Its normal Promise.all take an array of Promises, async function are of type function, but returns a Promise once invoked if no explicite return it will return a resolved promise with undefined value.

async function myAsyncFunction(){
  return 1; 
}

console.log(typeof myAsyncFunction)
console.log(typeof myAsyncFunction())
console.log(myAsyncFunction() instanceof Promise)

1 Comment

It's 4 am now and your answer become a clue to my current issue. Thanks!
1

You are returning a function from map callback, not a promise. instead return foo(num). then after flattening you have an array of promises.

const foo = (num) => new Promise(resolve => setTimeout(resolve, num * 1000)); // An async I/O function in actual code
array = [[1, 2, 3], [1, 2, 3] , [1, 2, 3]];

const promiseArray = array.map(arr => {
  return arr.map(foo); // its equal arr.map(num => foo(num));
}).flat();

const results = await Promise.all(promiseArray);
results.forEach(item => console.log(item));

3 Comments

How about the console.log statement? If I need to include more statements in the async function, how should I code it?
you can do it easily. as in one of the comments and Brunos answer mentioned, an async function should return a promise. you can do whatever you like and return a promise at the end(the job you did not do). and another problem is that you have to return arr.map(num => { (as I mentioned as a comment to Brunos answer)
even if you do not return a promise from an async function, whatever you return will be wrapped in a Promise.resolve(promisified) developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
0

An async function must return a promise. Your first implementation needs a return Statement:

const array = [[1, 2, 3], [1, 2, 3] , [1, 2, 3]];

const promiseArray = array.map(arr => {
  // returning the mapped list
  return arr.map(async (num) => {
    const result = await foo(num);
    console.log('Num: ' + num);
    // return at the end of async will generate a promise fulfillment for you.
    // see: https://developers.google.com/web/fundamentals/primers/async-functions
    return result;
  });
}).flat();

const result = await Promise.all(promiseArray);
console.log('result: ' + result);

6 Comments

You should return arr.map(num => { in order to make a 2D array and then flattening it.
Now this is still not producing an array of promises but an array of async functions.
Oh, I made a mistake in my answer too, good point @VLAZ
An async function always returns a promise. Whether you use the promise declaration or not. See: developers.google.com/web/fundamentals/primers/…
It returns a promise just if it's called, there is no invocation here, just a declaration
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.