1

How would I use a promise to wait for the asynchronous geocode method to return a result and then return either true or false?

function LocationValidator(value, element, paras) {
var geocoder = new google.maps.Geocoder();

geocoder.geocode({ 'address': value }, function (results, status) { // called asynchronously
    if (status == google.maps.GeocoderStatus.OK) {
        return true;
    } else {
        return false;
    }
});
}
chuckd
  • 13,460
  • 29
  • 152
  • 331
  • You want to make `LocationValidator` a blocking call basically? – Brad Christie Feb 26 '15 at 20:28
  • I want to block any return until geocoder.geocode has finished. THen it should return true or false depending on 'status' – chuckd Feb 26 '15 at 20:54
  • @user1186050: Promises don't block. [They're just callbacks](http://stackoverflow.com/a/22562045/1048572) in a pretty abstraction. – Bergi Feb 26 '15 at 20:55
  • Is there a way to prevent a return until the geocoder.geocode has finished? – chuckd Feb 26 '15 at 21:03
  • @user1186050: No, there is not. You have to live with asynchrony, and adopt all of your code that uses `LocationValidator` to work with it. – Bergi Feb 26 '15 at 21:07

2 Answers2

6

In the browser world you live in now, how you create your own promise unfortunately depends upon which promise library you are using. Here are a couple ways you could create a geocode(address) function that returns its result via a promise:

// jQuery promises
function geocode(address) {
    var geocoder = new google.maps.Geocoder();

    var def = $.Deferred();
    geocoder.geocode({ 'address': value }, function (results, status) { // called asynchronously
        if (status == google.maps.GeocoderStatus.OK) {
            def.resolve(results);
        } else {
            def.reject(status);
        }
    });
    return def.promise();
}

// ES6 style (supported by many polyfills, promise libraries and some browsers natively)
// my favorite library is Bluebird which this would work with
function geocode(address) {
    var geocoder = new google.maps.Geocoder();

    return new Promise(function(resolve, reject) {
        geocoder.geocode({ 'address': value }, function (results, status) { // called asynchronously
            if (status == google.maps.GeocoderStatus.OK) {
                resolve(results);
            } else {
                reject(status);
            }
        });
    });
}

Both can be used like this which is nice and simple:

geocode(address).then(function(result) {
    // code here that can use the result
}, function(errStatus) {
    // code here to handle an error
});

Note: you can't take an asynchronous operation like this and make it into a synchronous operation. You just can't do that in Javascript. So, you have to learn how to program with asynchronous operations and promises are one convenient way of doing that.


Other references:

Wrap Google map geocoder.geocode in a Promise

Promises in the Google APIs JavaScript Client Library

How do I convert an existing callback API to promises?

Community
  • 1
  • 1
jfriend00
  • 683,504
  • 96
  • 985
  • 979
1

One can "wrap" the geocode async call as follows - assuming the return value of the callback itself is irrelevant.

Note that this cannot be used to make the asynchronous call synchronous; but it does "wrap" the asynchronous callback to use a promise.

function geocodeWithPromise(data) {
    // Different frameworks provided different methods of creating and
    // using their promise implementation. Eg in jQuery the following would be:
    //   promise = $.Deferred();
    var promise = makePromise();
    geocoder.geocode(data, function (results, status) {
        if (status == google.maps.GeocoderStatus.OK) {
            promise.resolve(results, status);
        } else {
            promise.reject(status);
        }
    });
    return promise;
}

// The call is "synchronous" and a promise is returned immediately - 
// but done/fail are called later as they are [themselves callbacks and]
// part of the asynchronous promise.
geocodeWithPromise({ 'address': value })
  .done(function () { .. })
  .fail(function () { .. });

The tutorial JavaScript Promises: There and back again explains promises in more detail, and covers ES6-style promises.

(As jfriend keeps pointing out, the above code uses done/fail which are not required - as they can be implemented with then - and are only found in some promise implementations such as jQuery.)

user2864740
  • 60,010
  • 15
  • 145
  • 220
  • Nice promise abstraction. – Brad Christie Feb 26 '15 at 22:00
  • What's `makePromise()`? – jfriend00 Feb 28 '15 at 22:38
  • @jfriend00 An abstract method that returns "your flavor of 'a promise'" ;-) I've added a note to the posted code. – user2864740 Feb 28 '15 at 22:39
  • Interesting that it's not easy to make that `makePromise()` structure work with standard ES6 promises (they don't have resolve/reject methods) and with jQuery, you should return the promise, not the deferred. I just thought your abstraction should come with some explanation and perhaps some real-world examples so one who was not familiar with this level of detail could actually just use what you've provided. – jfriend00 Feb 28 '15 at 22:41
  • @jfriend00 I have not followed ES6 promises at all :> That's question creep, but I've added a link to an article (covering ES6) promises in general. – user2864740 Feb 28 '15 at 22:43
  • @user2864740 - they're already implemented in several browsers and are the general direction for the future and can be polyfilled already. It's likely worth knowing the ES6 direction if you program with promises much. jQuery is still the outlier because it's not very close to ES6 at all. Also `.done()` and `.fail()` are not ES6 compatible either. – jfriend00 Feb 28 '15 at 22:45
  • Your Promise/A comment isn't correct. Promise/A only describes the `.then()` behavior which is in ES6. Oddly, Promises/A never described how one creates a promise. – jfriend00 Feb 28 '15 at 22:47
  • @jfriend00 Yeah, I removed that - it was a mis-remembering on my part. I *thought* done/fail were in Promises/A (not A+), but when I went to add the link I saw I was wrong. – user2864740 Feb 28 '15 at 22:48