1

I'm trying to return describedIpList after all async operations are resolved. I can see a few ways to solve this without a promise, but I am interested in learning how to do this in best practice with a promise, do I really have to wrap another promise around my function and if so, please show me how to do this correctly.

 const ipResolver = (ips) => {
    const describedIpList = [];

    ips.map((ip) => {
        Promise.all([
            client.country(ip),
            client.city(ip)
        ])
            .then(response => {
                [Country, City] = response;
                describedIpList.push({
                    ip,
                    countryCode: Country.country.isoCode,
                    postalCode: City.postal.code,
                    cityName: City.city.names.en,
                    timeZone: City.location.timeZone,
                    accuracyRadius: City.location.accuracyRadius,
                });
                console.log(describedIpList);
            })
            .catch(err => describedIpList.push({ip, error: err.error}));
        return describedIpList;

    });

To reiterate. I am looking to return describedIpList after ips.map() resolves and then return a response res.json({fullyDescribedIps: ipResolver(ips)});

clusterBuddy
  • 1,523
  • 12
  • 39

2 Answers2

0

You'll need two Promise.alls - one to wait for all ip requests to resolve, and one to wait for the country and city of all of those to resolve.

const ipResolver = ips => Promise.all(
    ips.map(ip => Promise.all([
        client.country(ip),
        client.city(ip)
    ])
        .then(([Country, City]) => ({
            ip,
            countryCode: Country.country.isoCode,
            postalCode: City.postal.code,
            cityName: City.city.names.en,
            timeZone: City.location.timeZone,
            accuracyRadius: City.location.accuracyRadius,
        }))
        .catch(err => ({ ip, error: err.error }))
    )
);
CertainPerformance
  • 356,069
  • 52
  • 309
  • 320
  • Why not just `Promise.resolve`? For the outer Promise – yaakov Mar 09 '22 at 01:36
  • 2
    That wouldn't result in a Promise that resolves with the array of values - that'd result in an array of Promises that the consumer if `ipResolver` would then have to call `Promise.all` on to get to a useable result. Better to put it inside the function. – CertainPerformance Mar 09 '22 at 01:37
  • Ah, good point. – yaakov Mar 09 '22 at 01:38
  • Thanks for the answer @CertainPerformance, the function is returning an empty array (should return items), I'm assuming something is amiss with the promise there. Will you be editing this for an accepted answer by any chance? – clusterBuddy Mar 09 '22 at 01:44
  • @clusterBuddy There is no way that the code in my answer returns an empty array if the parameter array has any values - can you double check that you're using my code exactly? Runnable snippet with dummied up data: https://jsfiddle.net/51ohg3fq/ – CertainPerformance Mar 09 '22 at 01:45
  • @clusterBuddy As you can see, the Promise that gets returned doesn't resolve to an empty array – CertainPerformance Mar 09 '22 at 01:49
  • I just reverted back to my old function and printed to console in the `then()`, for sure it's supposed to return 3 objects. went back to your functions and `res.json({fullyDescribedIps: ipResolver(ips)});` returns `fullyDescribedIps:{}` – clusterBuddy Mar 09 '22 at 01:52
  • `ipResolver` returns a Promise, not the value the Promise resolves to. You must wait for the Promise to resolve, which is done with `.then`. You must do that before calling `res.json`. See https://stackoverflow.com/q/38884522 – CertainPerformance Mar 09 '22 at 01:53
  • @CertainPerformance - you are right, resolved the route response like this: `ipResolver(ips).then(describedIps => res.json(describedIps)` looks a bit wonky resolving a route from a promise resolve, but works. If you have a better way of executing this to resolve from an endpoint, would be happy. – clusterBuddy Mar 09 '22 at 02:01
  • @clusterBuddy Looks perfectly normal to me - that's a very typical-looking sort of route when you need to perform something asynchronous before sending, don't worry about how the syntax looks. – CertainPerformance Mar 09 '22 at 02:03
  • @clusterBuddy When in this sort of situation, but the `this` of the callback doesn't matter, you could simplify `fn.then(res => obj.cb(res))` to just `fn.then(obj.cb)` - but with Express, it does depend on having a calling context of the response, so there's no way to remove the inline callback here – CertainPerformance Mar 09 '22 at 02:17
0
async function ipInfo(ip) {
  try {
    const [Country, City] = await Promise.all([
      client.country(ip),
      client.city(ip),
    ]);
    // const [Country, City] = await client.countryAndCity(ip);
    return {
      ip,
      countryCode: Country.country.isoCode,
      postalCode: City.postal.code,
      cityName: City.city.names.en,
      timeZone: City.location.timeZone,
      accuracyRadius: City.location.accuracyRadius,
    }
  } catch (err) {
    return { ip, error: err.error };
  }
}

const ipResolver = Promise.all(ips.map(ipInfo));
// const ipsInfo = await Promise.all(ips.map(ipInfo));
rookie
  • 128
  • 4