Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Turn callback into promise

Tags:

javascript

I'm working with the google maps api and this piece of code is returning a list of places asynchronously. How can I call this function and have it trigger something when all the data is collected? This has been what i've tried so far -

$.search = function(boxes) {

    function findNextPlaces(place_results, searchIndex) {
        var dfd = $.Deferred();
        if (searchIndex < boxes.length) {
            service.radarSearch({
                bounds: boxes[searchIndex],
                types: ["food"]
            }, function (results, status) {
                if (status != google.maps.places.PlacesServiceStatus.OK) {
                    return dfd.reject("Request["+searchIndex+"] failed: "+status);
                }
                console.log( "bounds["+searchIndex+"] returns "+results.length+" results" );
                for (var i = 0, result; result = results[i]; i++) {
                    var marker = createMarker(result);
                    place_results.push(result.reference); // marker?
                }
            });
            return dfd.then(findNextPlaces);
        } else {
            return dfd.resolve(place_results).promise();
        }
    }

    return findNextPlaces([], 0);
};
like image 920
seasick Avatar asked Feb 23 '14 10:02

seasick


People also ask

How do I change my callback to promise?

If the callback function returns non-error output, we resolve the Promise with the output. Let's start by converting a callback to a promise for a function that accepts a fixed number of parameters: const fs = require('fs'); const readFile = (fileName, encoding) => { return new Promise((resolve, reject) => { fs.

How do I return a promise from a callback function?

To convert a callback into a promise, you need to return a promise. You run the code with the callback inside the promise. const readFilePromise = () => { return new Promise((resolve, reject) => { fs.


1 Answers

To answer the question implied by the title, "Turn callback into promise", the simple answer is to use a really simple "promisifier pattern" (my term), in which a Deferred's .resolve() method is established as a callback :

Original call with callback :

obj.method(param1, param2, ... paramN, callbackFn);

Converted call, with Deferred wrapper :

$.Deferred(function(dfd) {
    obj.method(param1, param2, ... paramN, dfd.resolve);
}).promise();

This can be done whether or not obj.method is asynchronous. The advantage is that you now have the full chainability of Deferreds/promises available to you either in the same block of code or, more typically, elsewhere having assigned or returned the generated Promise.

Here's a way in which the pattern might be used in the case of this question ...

$.search = function(boxes, types) {

    function findPlaces(box) {
        var request = {
           bounds: box,
           types: types
        };

        //***********************
        // Here's the promisifier
        //***********************
        return $.Deferred(function(dfd) {
            service.radarSearch(request, dfd.resolve);
        }).promise();
        //***********************
        //***********************
        //***********************
    }

    function handleResults(results, status, searchIndex) {
        var message, marker;
        if (status != google.maps.places.PlacesServiceStatus.OK) {
            message = "bounds[" + searchIndex + "] failed : " + status;
        }
        else {
            message = "bounds[" + searchIndex + "] returns " + results.length + " results";
            for (var i = 0, result; result = results[i]; i++) {
                marker = createMarker(result);
                place_Results.push(result.reference);
            }
        }
        return message;
    }

    var place_Results = [],
        p = $.when();//resolved starter promise

    //This concise master routine comprises a loop to build a `.then()` chain.
    $.each(boxes, function(i, box) {
        p = p.then(function() {
            return findPlaces(box).done(function(results, status) {
                // This function's arguments are the same as the original callback's arguments.
                var message = handleResults(results, status, i);
                $('#side_bar').append($("<div/>").append(message));
            });
        });
    });

    //Now, chain a final `.then()` in order to make the private var `place_Results` available via the returned promise. For good measure, the `types` array is also repeated back.
    return p.then(function() {
        return {
            types: types,
            results: place_Results
        };
    });
};

$.search() can now be used as follows :

$.search(boxes, ["food"]).done(function(obj) {
    alert(obj.results.length + ' markers were a added for: "' + obj.types.join() + '"');
});

DEMO - Note: jQuery 1.8.3+ is required due to major revision of jQuery.Deferred.then() at jQuery 1.8.3 .

This is not exactly equivalent to the code in the question but may be good for diagnosis of the issues you report. In particular, :

  • it won't stop on error
  • it will put success and error messages in the '#side_bar'.

It should be simple enough to adjust the code to do what you want.

like image 200
Beetroot-Beetroot Avatar answered Sep 22 '22 23:09

Beetroot-Beetroot