Dark Knight
Dark Knight

Reputation: 41

Avoid using await in a for loop when Promise result updates the iterating loop variable itself

Is there any way to not use await inside loop for following code?

const redirects = ['redirectId1'];

for (let i = 0; i < redirects.length; i++) {
  const promiseResult = await anAsyncFunction(redirects[i]);
  
  if (promiseResult.redirects) {
    redirects.forEach(newRedirect => {
      if (!redirects.includes(newRedirect)) redirects.push(newRedirect);
    });
  }
}

I've tried using Map

const redirects = ['redirectId1'];

const promiseArr = redirects.map(async redirect => {
  const promiseResult = await anAsyncFunction(redirect);

  if (promiseResult.redirects) {
    redirects.forEach(newRedirect => {
      if (!redirects.includes(newRedirect)) redirects.push(newRedirect);
    });
  }
});

await Promise.all(promiseArr);

but in doing so, code executes further before newRedirect is pushed to redirects array thus not calling anAsyncFunction for new added value.

I want to know if there any possible way to achieve this?

Upvotes: 1

Views: 47

Answers (2)

Lior Kaminsky
Lior Kaminsky

Reputation: 417

If u are interested that each redirect batch will be in parallel and keep asking for more redirects as long as there is, u can achieve it as following :

const handleRedirects = async (_redirects) => {
  const newRedirects = [];
  await Promise.all(
    _redirects.map(async (redirect) => {
      const promiseResult = await anAsyncFunction(redirect);

      if (promiseResult.redirects) {
        promiseResult.redirects.forEach((newRedirect) => {
          if (!_redirects.includes(newRedirect)) newRedirects.push(newRedirect);
        });
      }
    })
  );
  return newRedirects;
};


const run = async () => {
  const redirects = ['redirectId1'];
  let newRedirects = [ ...redirects ];
  do {
    newRedirects = await handleRedirects(newRedirects);
    redirects.push(...newRedirects);
  } while (newRedirects.length)
}

Upvotes: 0

CertainPerformance
CertainPerformance

Reputation: 370989

If the idea is to send out each request as soon as possible, without waiting for earlier requests to resolve first, you can use a recursive async function that calls itself when the result contains redirects:

const recurse = (redirects) => Promise.all(
  redirects.map(r => anAsyncFunction(r)
    .then(({ redirects }) => {
      if (redirects) {
        return recurse(redirects);
      }
    })
  )
);
// pass in the initial array:
await recurse(redirects);

If you also need to use the populated outer redirects array later, then:

const recurse = (redirects) => Promise.all(
  redirects.map(r => anAsyncFunction(r)
    .then(({ redirects }) => {
      return redirects
        ? recurse(redirects).then(res => [redirects, res])
        : [];
    })
  )
);
// pass in the initial array:
const allRedirects = [initialRedirects, await recurse(initialRedirects)].flat(Infinity);

Upvotes: 1

Related Questions