ValleP
ValleP

Reputation: 11

How to return a array with objects from a nested async callback function inside a for of loop?

I'm trying to write a small program that will ping internal addresses to check if a server is down. The problem is that I cant get the timing right. If I put a console.log() inside the for of loop it returns the objects I want but several times of cource. If I write a console.log after the for of loop, I only get an empty array, probably because the loop is not finished yet.I have tried to fix this in various ways but keep running into problems. I use node-cmd to send an http ping as normal pings only work against domains.

You can ignore my nightmare regex :) It should just split the response into an array which is then pushed to object

var cmd = require('node-cmd');

const hosts = [
  'https://adressone.com',
  'https://adresstwo.com',
  'https://adressthree.com',
  'https://adressfour.com'
];
let results = [];
for (let host of hosts) {
  cmd.run(`http-ping.exe ${host}`,
    async function(err, data, stderr) {
      const result = await data.split('>');
      const resultDig = result[1].replace(/[^0-9\.]+/g, " ").replace(/2$/g, "").trim().split(' ')

      results.push({
        host: host,
        status: resultDig[0],
        bytes: resultDig[1],
        time: resultDig[2],
      });

    }
  );
}
console.log(results);

The result I would like in console.log(results):

[{
    host: 'https://adressone.com',
    status: '200',
    bytes: '703',
    time: '640'
  },
  {
    host: 'https://adresstwo.com',
    status: '200',
    bytes: '703',
    time: '640'
  },
  {
    host: 'https://adressthree.com',
    status: '200',
    bytes: '703',
    time: '640'
  },
  {
    host: 'https://adressfour.com',
    status: '200',
    bytes: '703',
    time: '640'
  }
]

Upvotes: 1

Views: 591

Answers (2)

Nitheesh
Nitheesh

Reputation: 20006

The best approach for this question will be by using Promises in JavaScript.

An example for this implementation.

async function parseResult() {
  const hosts = [
    "https://adressone.com",
    "https://adresstwo.com",
    "https://adressthree.com",
    "https://adressfour.com",
  ];
  const promises = [];
  for (let host of hosts) {
    const response = new Promise(function (resolve, reject) {
      setInterval(async () => {
        resolve({
          status: "Success",
        });
      }, 1000);
    });
    promises.push(response);
  }
  const finalResult = await Promise.all(promises);
  return finalResult;
}
parseResult().then((res) => console.log(res));

OR

async function parseResult() {
  const hosts = [
    "https://adressone.com",
    "https://adresstwo.com",
    "https://adressthree.com",
    "https://adressfour.com",
  ];
  const promises = hosts.map(
    (host) =>
      new Promise(function (resolve, reject) {
        setInterval(async () => {
          resolve({
            status: "Success",
          });
        }, 1000);
      })
  );
  const finalResult = await Promise.all(promises);
  return finalResult;
}
parseResult().then((res) => console.log(res));

A Pseudo code for your question will be like below

var cmd = require("node-cmd");
async function parseResult() {
  const hosts = [
    "https://adressone.com",
    "https://adresstwo.com",
    "https://adressthree.com",
    "https://adressfour.com",
  ];
  let promises = [];
  for (let host of hosts) {
    const response = cmd.run(
      `http-ping.exe ${host}`,
      async function (err, data, stderr) {
        const result = await data.split(">");
        const resultDig = result[1]
          .replace(/[^0-9\.]+/g, " ")
          .replace(/2$/g, "")
          .trim()
          .split(" ");

          // Returning a promise here
        return new Promise(function (resolve, reject) {
          resolve({
            host: host,
            status: resultDig[0],
            bytes: resultDig[1],
            time: resultDig[2],
          });
        });
      }
    );
    promises.push(response);
  }
  // promises array will hold a list of promises now
  // Await till all the promises are resolved and that will be your final result
  const finalResult = await Promise.all(promises);
  return finalResult;
}
parseResult().then((res) => console.log(res));

Explanation: Here the parseResult function loops through an array hosts and will run an async operation against each nodes in hosts array. The result of this operation is a Promise, so your final promises array will be holding a list of promises. Once all this promises are resolved, you will get your final result. You can detect when all your promises are resolved by using await Promise.all(promises)

Upvotes: 1

some-user
some-user

Reputation: 4924

I'd suggest to use node-cmd-promise. I removed the splitting:

const cmd = require('node-cmd-promise')

const hosts = [
  'https://adressone.com',
  'https://adresstwo.com',
  'https://adressthree.com',
  'https://adressfour.com',
]

async function main() {
  const results = []
  for (const host of hosts) {
    const result = await cmd(`echo ${host}`)

    // Insert logic here
    const resultDig = [result.stdout, 'foo', 'bar']

    results.push({
      host,
      status: resultDig[0],
      bytes: resultDig[1],
      time: resultDig[2],
    })
  }
  console.log(results)
}

main()

Upvotes: 0

Related Questions