-1

Firstly I know there is already a lot of stuff about async on here, but I have looked in many places on and off SO and not found anything which seems to answer this particular question, at least not to the untrained eyes of this noob.

Background

I have a function foo which depends on the result of another function googlePlaces which contains a promise, but the overall function is not a promise.

JS

var googleMapsClient = require('@google/maps').createClient({
  key: <API_KEY>
  Promise: Promise
});
...
function foo() {
  
  var point = [49.215369,2.627365]
  var results = googlePlaces(point)
    console.log('line 69')
    console.log(results)
    
    // do some more stuff with 'results'
 
   }

function googlePlaces(point) {

    var placesOfInterest = [];

    var latLng = (point[0]+','+point[1])
    var request = {
      location: latLng,
      radius: 10000
    };

  
    googleMapsClient.placesNearby(request).asPromise()
    
    .then(function(response){        
       placesOfInterest.push(response.json.results)      
       }) 

    .finally(function(){
       console.log('end of googlePlaces function:')
       console.log(placesOfInterest);
       return(placesOfInterest);
       })
    
}

console log:

line 69
undefined
end of googlePlaces function
[my lovely array]

What I've tried

I've tried making foo an async function, and changing results to be = await googlePlaces(point), but I still get undefined, and also it throws UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)

I can move everything from foo after line 69 to be within the googlePlaces function, and that works, but for neatness and readability of code it would be much better if it could stay in foo

Community
  • 1
  • 1
Chris A
  • 863
  • 1
  • 10
  • 31

3 Answers3

3

In googlePlaces() do:

return googleMapsClient.placesNearby(request).asPromise();

then in foo() do:

async function foo(){
  ...    
  var results = await googlePlaces(point);
  results.then( /* do you then stuff here */);
}
Randy Casburn
  • 13,840
  • 1
  • 16
  • 31
  • Yes, that's done it! So, from analysing this, is it safe to say that if you have a promise within a function which is being called from elsewhere in the code, then it's best for the promise to *be* the return of that function, rather than returning something *after* the promise? – Chris A Jun 03 '19 at 21:32
  • Yes, that is a good way to think about it. That will also make exception handling easier to because you can start to centralize that part. As you continue to think about Promises and designing you moving parts, think about _Action_ code (getGooglePlaces), _Model/Entity_ code (Places) and _Control_ code (foo) the parts to pull stuff together. – Randy Casburn Jun 03 '19 at 21:37
  • Another small breakthrough in my fledgling development journey! I will try and follow those principles of action, model and control. Thank you! – Chris A Jun 03 '19 at 21:41
0

If you are sure that foo returns a promise, Promisfy it to chain with then:

function foo() {

  var point = [49.215369,2.627365]
  var results = googlePlaces(point)
   return results;
 }

(new Promise(function(res){
     res(foo());
})).then(function(result){
//do something with result..
})
ibrahim tanyalcin
  • 5,643
  • 3
  • 16
  • 22
  • nesting promises is an antipattern: https://stackoverflow.com/questions/23803743/what-is-the-explicit-promise-construction-antipattern-and-how-do-i-avoid-it – Randy Casburn Jun 03 '19 at 21:20
  • Watch the parentheses there, I'm still having un-nested chain, the following thenable is the top level one. I do not know what foo returns, do I? Is it better to fit coding standards or be on the safe side by wrapping it with res(foo). Hurts no one. – ibrahim tanyalcin Jun 03 '19 at 21:26
0

Without having to change your code too much you can wrap the google places promise within another promise that bubbles up to foo(). From there you can handle the results.

function foo() {

    var point = [49.215369,2.627365]
    var promise = googlePlaces(point)

    promise.then((results) => {
        // do stuff with 'results'
        console.log(results)
    });

}

function googlePlaces(point) {

    var placesOfInterest = [];

    var latLng = (point[0]+','+point[1])
    var request = {
      location: latLng,
      radius: 10000
    };

    return new Promise((resolve) => {
        googleMapsClient.placesNearby(request).asPromise();
        .then(function(response){        
           placesOfInterest.push(response.json.results)      
           }) 

        .finally(function(){
           console.log('end of googlePlaces function:')
           console.log(placesOfInterest);
           // resolve the promise 
           resolve(placesOfInterest);
           })
    });
}
Noremac
  • 554
  • 2
  • 7
  • nesting promises is an antipattern. https://stackoverflow.com/questions/23803743/what-is-the-explicit-promise-construction-antipattern-and-how-do-i-avoid-it – Randy Casburn Jun 03 '19 at 21:18
  • Better than having to move their logic out of the googlePlaces and potentially end up duplicating that logic later on – Noremac Jun 03 '19 at 21:20