1

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 
})
Getter Jetter
  • 2,033
  • 1
  • 16
  • 37
  • It's really unclear why you want to use `reduce`. Shouldn't you build your result with `map` and possibly `filter`? Then you can trivially use `Promise.all`. – Bergi Apr 22 '18 at 12:58
  • @Bergi As far as I know `map` returns an array with equal length as the original, in my case I have to filter out some items. But you're right, maybe I will give `filter` a try even if my issue is solved now. – Getter Jetter Apr 22 '18 at 14:04

1 Answers1

1

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.

Jared Smith
  • 19,721
  • 5
  • 45
  • 83
  • Thx for your answer. with your method html2 now returns a request object, but should return a string containing the requested html. Also when logging newThreads I get "Promise { }". Do I have to use one of these request-promise packages? – Getter Jetter Apr 22 '18 at 12:30
  • @OlivierKrull You cannot immediately return the result string from an asynchronous function. Just wait for the promise. – Bergi Apr 22 '18 at 12:59
  • Had to make some adaptations to your code, but basically this is the right answer thx – Getter Jetter Apr 22 '18 at 13:50