Search code examples
javascriptasynchronouses6-promisereducenpm-request

Await async functions to be done in reduce() function


I have a fuction which includes the following:

const newThreads = newItems.reduce( (acc, item) => {
  request(item.href, function(error2, response2, html2){

    if(!error2) {

      const thread = cheerio.load(html2)
      const today = thread('div#ignmsgbttns1').parent().parent().find("b:contains('Today')")

      if(today.text()) {
        acc.push(item)
      }

    }
  })
  return acc
}, [])

console.log(newThreads)

Of course the log returns an empty array, because of the async stuff (request) executed in the reduce loop.

So what I would like to do is:

const newThreads = await newItems.reduce( etc...

And wait for the requests in the reduce loop to be done.

But I don't get my head around how to do it properly.

So I know I have to use async, await or promises, but don't know how to do it.

I think the reduce callback also has to be async but absolutely not sure on this point.

The request method comes from the npm request package , they also provide some packages to use promises, but to be honest, I don't know how to apply it with reduce.

I'm pretty sure there is already a similar question somewhere but couldn't find it.

Any help would be greatly appreciated.

ps: for those wondering what cheerio is, here the link.

Final code after applying answer

I had to use the async-request package

const newThreads = newItems.reduce(async (acc, item) => {
  const current = await acc;

  const html2 = await requestAsync(item.href);
  const thread = cheerio.load(html2.body);

  const today = thread('div#ignmsgbttns1')
    .parent()
    .parent()
    .find("b:contains('Today')");

  if (today.text()) current.push(item);

  return current;
}, []);

newThreads.then((res) => {  
  //..doing stuff with res 
})

Solution

  • In order to make this work you'll need the Promise returning version.

    const newThreads = newItems.reduce(async (acc, item) => { // note async
      const current = await acc; // unwrap the previous Promise
      try {
        const html2 = await request(item.href); // unwrap request Promise
        const thread = cheerio.load(html2);
        const today = thread('div#ignmsgbttns1')
          .parent()
          .parent()
          .find("b:contains('Today')");
    
        if (today.text()) current.push(item);
      } catch (error2) {
        // do whatever
      }
      return current;
    }, []);
    

    The newThreads variable will be a Promise of an array of items that passed the conditional check.