10

I would like to call the Google Maps Geocoding API using a Promise like this:

function makeGeoCodingRequest(address,bounds)
{
    /*
        Input parameters:
            address:a string
            bounds: an object of class google.maps.LatLngBounds(southWest,northEast)

        This will return a set of locations from the google geocoding library for the given query
     */
    var url="https://maps.googleapis.com/maps/api/geocode/json?address=" + address + "&key=AIzaSyD9GBloPC20X-1kWRo7sm_0z5xvCiaSd3c";
    var promise,response;
    var messages={
            "ZERO_RESULTS":"No results were found",
            "OVER_QUERY_LIMIT":"We are over the query limit.Wait awhile before making a request",
            "REQUEST_DENIED":"Request was denied,probably using a bad or expired API Key",
            "INVALID_REQUEST":"Request was sent without the required address,component or component",
            "UNKNOWN_ERROR": "There was an error somewhere on Google's servers" 
    };
    if(address)
        promise=Q($.ajax({
            type: "GET",
            url: "https://maps.googleapis.com/maps/api/geocode/json?address=" + address + "&key=API_KEY"
        }));
        return promise.then(function(data) {
            if (data.status === "OK") return data;
            else    console.error(messages[data.status]);
            return null;    
        });
}

When I call the function makeGeoCodingRequest request,I find that I obtain a promise instead of a value:

 var geo=makeGeoCodingRequest(address);
 console.log(Q.isPromise(geo));//returns true

Why isnt promise.then executed before the value was returned? How can I obtain a value from this promise instead of another promise?

Willem D'Haeseleer
  • 19,661
  • 9
  • 66
  • 99
vamsiampolu
  • 6,328
  • 19
  • 82
  • 183
  • promise don't give out value, instead you pass in the function that is going to use the value into the promise using `.then()` – Hank W Aug 30 '20 at 08:38

4 Answers4

6

If you depend on a promise in order to return your data, you must return a promise from your function.

Once 1 function in your callstack is async, all functions that want to call it have to be async as well if you want to continue linear execution. ( async = return a promise )

Notice that your if statement does not have braces and thus only the first statement after it will not be executed if the condition fails.

I fixed it in this example. Notice the remarks I added.

if(address){
    promise=Q($.ajax({
        type: "GET",
        url: "https://maps.googleapis.com/maps/api/geocode/json?address=" + address + "&key=API_KEY"
    }));
    return promise.then(function(data) {
        // whatever you return here will also become the resolve value of the promise returned by makeGeoCodingRequest
        // If you don't want to validate the data, you can in fact just return the promise variable directly
        // you probably want to return a rejected promise here if status is not what you expected
        if (data.status === "OK") return data;
            else console.error(messages[data.status]);
        return null;    
    });
}

You must call makeGeoCodingRequest in the following fashion.

makeGeoCodingRequest(address,bounds).then(function(data){
    // this will contain whatever 
    console.log(data);
});
Willem D'Haeseleer
  • 19,661
  • 9
  • 66
  • 99
  • If I instead deciede to use `promise.then(...)` and `return promise` and then use `makeGeoCodingRequest(address,bounds).then(...)`,would it still be the same? – vamsiampolu Aug 27 '14 at 15:15
  • Yes, The promise returned by `makeGeoCodingRequest` will have the same value of the return value inside your `promise.then` handler. You can reject by returning something like `Q.reject(new Error("Http request failed" + data.status))` – Willem D'Haeseleer Aug 27 '14 at 15:23
  • 1
    Thank you for the answer,everything works,including Q.reject. – vamsiampolu Aug 27 '14 at 15:40
3

I find that I obtain a promise instead of a value

Yes, because the operation is asynchronous and returns a promise which represents the future value.

Why isnt promise.then executed before the value was returned?

Because it's asynchronous. .then will (must) never execute its callback before it returns another promise.

How can I obtain a value from this promise instead of another promise?

You are getting the value in the callback:

makeGeoCodingRequest(address).then(function(geo) {
    console.log(geo)
    console.log(Q.isPromise(geo)); // false
    // do anything with the value here
})
// if you need to do anything with the result of the callback computation:
// you're getting back another promise for that!

It's impossible to obtain it synchronously from the promise (many have tried). That would mean blocking execution, which we don't want - let's stay async and non-blocking!

Community
  • 1
  • 1
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • Very well explained. It might be interesting to give references to 'how to return the response from an AJAX call' and other questions relating to the issue. – Benjamin Gruenbaum Aug 27 '14 at 15:17
  • What you are essentially saying is that any thing you want to do must be done inside the `then` method using the `data` I obtain as an `argument` in the `callback` to it.Just like `callbacks`,I guess. – vamsiampolu Aug 27 '14 at 15:46
  • 1
    Yes, [just like callbacks](http://stackoverflow.com/a/22562045/1048572). Only that you get another promise back for the result of the callback, which makes them chainable. – Bergi Aug 27 '14 at 15:48
  • And you get the possibility of unified error handling thrown in for free. – Roamer-1888 Aug 28 '14 at 01:04
0

You are returning a promise from the function makeGeoCodingRequest. That is a good thing according to me, this helps you chain any more async calls if required in future. What I would suggest is to use a .then() on the returned promise and check if the promise has any returned value or error in the following manner.

var geoPromise = makeGeoCodingRequest(address); 
geoPromise.then(
   onSuccess(result){
       // You can use the result that was passed from the function here.. 
   }, 
   onFailure(response){
      // Handle the error returned by the function.
   }
);
inxss
  • 85
  • 2
  • 7
-1

If you want your code to wait until the asynchronous action is completed before continuing (BAD IDEA - the page will freeze while the request is completing) then add async: false to the ajax request's parameters.

My recommendation, though, is to have makeGeoCodingRequest return nothing directly - and simply give it an extra argument, requestCallback so that its caller can pass in a function that will be called when the data is available. Call that function, with the resulting data, inside of your promise.then function.

Katana314
  • 8,429
  • 2
  • 28
  • 36
  • If there's a problem with my answer, perhaps someone could give a suggestion to improve it? – Katana314 Aug 27 '14 at 15:04
  • Your recommendation of making sync requests does not address the fact that OP is not understanding the promises concept. Besides that, it's wrong. You need to set a `async:false` in order to achieve a sync request. Like you noticed your self, in almost any circumstance, that's a bad idea. Introducing a callback in the function is also a bad idea, as clearly `makeGeoCodingRequest` should just return a promise, mixing both is an anti pattern and confusing for a beginner. – Willem D'Haeseleer Aug 27 '14 at 15:10
  • Ah, correct; I got the async value wrong. That part was actually to give an explanation of why his code can't simply return a value so easily. I guess I wasn't personally familiar with ideologies of which functions should continue to use the given 'promise' object, and which should transfer to a simplistic function; so that's actually new for me. I did not suggest he should use both. – Katana314 Aug 27 '14 at 15:17
  • Your answer is still suggesting he should use both. – Willem D'Haeseleer Aug 27 '14 at 15:19
  • No it isn't. Please read it more closely. The first paragraph begins with "If you want your code to wait..." and also denotes that's a bad idea. The second paragraph is an alternative. – Katana314 Aug 27 '14 at 15:29
  • The first paragraph suggest something the OP definitely shouldn't be doing, it should not even be mentioned in your answer. The second paragraph suggests mixing promises with callbacks. ( your adding a callback to a function that returns a promise) That is an anti pattern. You need to reformat your answer entirely, or delete it, sorry... – Willem D'Haeseleer Aug 27 '14 at 15:31
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/60096/discussion-between-katana314-and-willem-dhaeseleer). – Katana314 Aug 27 '14 at 15:31