0

I'm struggling with .map loop and async function inside. I use request-promise for async requests.

  import * as rp from 'request-promise';

  const testArray = ['one', 'two', 'three'];
  const link = 'https://somelink.com/';

  const test = testArray.map(async (elem) => {
    console.log('before', elem);

    await rp.get(link)
      .then(async () => {
        console.log('success');
      });

    console.log('after', elem);
  });

  Promise.all(test);

The output of this code:

before one
before two
before three
success
after one
success
after three
success
after two

What I need is that the code to execute in the correct order with output like this:

before one
success
after one
before two
success
after two
before three
success
after three

Can't figure out what am I doing wrong. Please help.

tjnk24
  • 180
  • 1
  • 4
  • 11
  • `Promise.all` will give you the results in order, but your not either returning anything in your map, and not using the results from`Promise.all` too. Like jfriend says for serial execution `for of` would be good. – Keith Oct 18 '20 at 08:18

1 Answers1

3

.map() is NOT async aware. It will not pause its loop for an await in the callback function you pass it. Instead, the await will just immediately cause the async function to return an unresolved promise and the .map() will go right ahead with the other iterations of the loop. As you already seem to know the resulting array from .map() will just be an array of those promises.

If you want the loop to pause and wait for the await, so you can truly sequence your asynchronous operations, then use a plain for loop, not a .map() loop.

 import * as rp from 'request-promise';

  const testArray = ['one', 'two', 'three'];
  const link = 'https://somelink.com/';

  for (let elem of testArray) {
    console.log('before', elem);

    await rp.get(link)
      .then(async () => {
        console.log('success', elem);
      });

    console.log('after', elem);
  });

This will then execute your rp.get() operations in sequence waiting for the first one to complete before the second one is executed. Your .map() loop was executing them all in parallel which means you do not control the order of execution.


FYI, the request() library and the corresponding derivatives of it have been deprecated and will no longer be actively developed to add new features. There is a list of alternatives here that are recommended for all new projects. My favorite is got() which was built from the ground up to use promises, but you can pick whichever one has the features you want and/or an API you like.

jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • Thanks! It works! But is there any replacement of "for...of" loop? Because my eslint throws no-restricted-syntax error – tjnk24 Oct 19 '20 at 06:46
  • @tjnk24 - Why is eslint complaining about a `for/of` loop? Perhaps want to fix your eslint settings. That is modern JS syntax. You can use an old-fashioned for loop if you want `for (let i = 0; i < testArray.length; i++) { ... }` and then refer to `testArray[i]` in the loop, but `for/of` is a newer and better way to program. – jfriend00 Oct 19 '20 at 21:16