2

I have the following object, stored in a variable ($gameSystem._ipLookupJSON):

{
    "www.geoplugin.net/json.gp?jsoncallback=?": {
        "IP": "geoplugin_request",
        "Country": "geoplugin_countryCode",
        "City": "geoplugin_city"
    },

    "gd.geobytes.com/GetCityDetails?callback=?": {
        "IP": "geobytesipaddress",
        "Country": "geobytescountry",
        "City": "geobytescity"
    },

    "ip-api.com/json": {
        "IP": "ip",
        "Country": "country_name",
        "City": "city"
    },

    "ipinfo.io/json": {
        "IP": "ip",
        "Country": "country",
        "City": "city"
    }
}

Each of the keys in this object is a URL.

I have a function ($._findService()) that:

  1. Goes through each of these keys and sends them to another function ($._urlExists()), which checks if the URL is valid / responsive,

  2. If true, $._findService() creates a new array with only the key and its elements,

  3. And is supposed to return this new array.

Unfortunately, I am having problems with the third step- returning the new array.

I have Google'd and read as much as I can about Promises, .then, and Async/Await, but I just cannot figure it out and am at my wit's end just staring at these lines of code.

const isServiceAvailable = async url_to_check => {
  console.log(url_to_check);
  return await subaybay.an._urlExists("http://" + url_to_check);
};

const checkServices = async(json_data) => {
    return await Promise.all(Object.keys(json_data).map(url_to_check => isServiceAvailable(url_to_check)));
};

$._findService = function(json_data) { 
  var url_check = checkServices(json_data); 
  url_check.then(function(values) { 
    for (i = 0; i < values.length; i++) { 
      if (values[i] === true) { 
        var service_to_use = new Promise(function(resolve, reject) {
          var result = [];
          result.push(json_data[Object.keys(json_data)[i]]);
          result.unshift(Object.keys(json_data)[i]);
          resolve(result);
        });
        service_to_use.then(function(value) {
          console.log(value);
          return value;
        });
      }; 
    }; 
  }); 
};

I am hoping for $._findService() to return an array.

But alas all I get is undefined.

I apologize if my code is not elegant or pretty- I have only been teaching myself JavaScript since the end of February.

Heretic Monkey
  • 11,687
  • 7
  • 53
  • 122
PE B
  • 65
  • 4
  • Possible duplicate of [How do I access previous promise results in a .then() chain?](https://stackoverflow.com/questions/28250680/how-do-i-access-previous-promise-results-in-a-then-chain) – Randy Casburn Jun 09 '19 at 00:27
  • Yes, inside "then" its another context, the promise context. Meaning that any return will return on that context, not in the function you called. – filipe Jun 09 '19 at 00:31
  • Possible duplicate of [How do I return the response from an asynchronous call?](https://stackoverflow.com/questions/14220321/how-do-i-return-the-response-from-an-asynchronous-call) – Heretic Monkey Jun 09 '19 at 01:34
  • @filipe - thanks, where should i put the return so that $._findService() returns 'value'? – PE B Jun 09 '19 at 02:08
  • I have posted an answer for that question.. – filipe Jun 09 '19 at 03:46

2 Answers2

1

Your problem was that you wasn't returning anything in the function scope and you should have return the promise(s).

const isServiceAvailable = url_to_check => subaybay.an._urlExists("http://" + url_to_check);

const checkServices = urls => Promise.all(urls.map(url_to_check => {
    return {url: url_to_check,status: isServiceAvailable(url_to_check)}
}));

$._findService = async function(json_data) {
    const values = await checkServices(Object.keys(json_data));
    return values.filter(v => v.status).map(v => v.url);
};

You can then use:

const result = await $._findService(json_data)

or

$._findService(json_data).then(result => { /* Do something */ })

Note: when you return something from an async function you will get a promise, so, when you use await you are awaiting for the promise result inline.

There isn't and never will be any disadvantage to use async and await over a Promise, and, it's modern and better since you don't make more nested functions using "then" or "new Promise" syntax.

filipe
  • 1,957
  • 1
  • 10
  • 23
  • 1
    Thank you @filipe! That works! I have been toiling away at this code for two days- I really appreciate your help! Many thanks! – PE B Jun 09 '19 at 02:36
0

I would recommend a slight change to checkServices. Currently, the input type is an object but the output is promise of array. I think it would be more intuitive to return a promise of object, matching the original input -

// old function
checkServices({ "/foo": ..., "bar": ... })
// => Promise [ true, false ]

// new function
checkServices({ "/foo": ..., "bar": ... })
// => Promise { "/foo": true, "/bar": false }

Here's the changes -

// old function
const checkServices = async(json_data) => {
    return await Promise.all(Object.keys(json_data).map(url_to_check => isServiceAvailable(url_to_check)));
};

// new function
const checkServices = (o = {}) =>
  Promise.all(
    Object.keys(o).map(k =>
      isServiceAvailable(k).then(v => [ k, v ])
    )
  )
  .then(Object.fromEntries)

With this result, it's easy to find all true keys for an object -

$._findService = (o = {}) =>
  checkServices(o).then(o =>
    Object.keys(o).filter(k => o[k])
  )

$._findService({ "/foo": ..., "bar": ... })
// Promise [ "/foo" ]

Expand the snippet below to run this program in your browser -

const checkServices = (o = {}) =>
  Promise.all(
    Object.keys(o).map(k =>
      isServiceAvailable(k).then(v => [ k, v ])
    )
  )
  .then(Object.fromEntries)

// fake implementation for demo
const isServiceAvailable = (service = "") =>
  new Promise (r =>
    setTimeout (r, 1000, service === "/foo")
  )

_findService = (o = {}) =>
  checkServices(o).then(o =>
    Object.keys(o).filter(k => o[k])
  )

checkServices({ "/foo": 1, "/bar": 2 }).then(console.log, console.error)
// { "/foo": true, "/bar": false }

_findService({ "/foo": 1, "/bar": 2 }).then(console.log, console.error)
// [ "/foo" ]

There is no advantage to using async-await in this program

Mulan
  • 129,518
  • 31
  • 228
  • 259