Search code examples
javascriptarraysnode.jsgeocodinges6-promise

Using Promise constructor to transform each element in an array and return the element back


I'm using Geocodio, it is callback based geocoding. I have an array of objects, each has a restaurant object nested in it. See example below:

{
    id: 0, title: "Half off all pizzas", 
    details:"Carry out only, all-day", 
    days: ["Monday", "Tuesday"],
    restaurant: {
        name: "Papa John's", 
        addressOne: "2937 Greenville Ave", 
        city: "Dallas", 
        state: "TX", 
        zip: 75206
    } 
}

I currently am mapping over the array of objects to pass each restaurant's address into a geocoding function. I want to then add a property to that object called location, which will contain the results of the geocoding. However, the element is being returned before the results of the function can be passed into the object. See below:

generateCoordinates (req, res, next) {
    var newDeals = deals.map((cur, ind, arr) => {
        location = new Promise (function(resolve, reject) { 
            geocodio.get('geocode',  {q: `${cur.restaurant.addressOne}, 
                ${cur.restaurant.city}, ${cur.restaurant.state}, 
                ${cur.restaurant.zip}`}, function(err, response) {
                    if (err) {
                        reject(err)
                        throw err;
                    }
                    else {
                        var result = JSON.parse(response);
                        let obj = result.results[0].location;
                        // console.log(obj);
                        resolve(obj)
                    }
                }
            )}).then((obj) => {
                cur.location = obj;
                console.log(cur)
                return cur;
            })
        return cur;
        // console.log(newDeals)
    }) 
    res.status(200).send(newDeals)
},

There's a good chance I'm misusing the Promise constructor here. The console.log in the .then shows each element having the location property properly added, but the response and console.log(newDeals) shows the value as "Promise".

How can I set the location property of each element in the array?


Solution

  • Yes, you shouldn't do anything in the async callback but to call resolve or reject. Especially you should not throw!

    The return cur doesn't exactly work, because it happens before the promise will be resolved. You'll need to return the promise from the map callback and then wait for all the promises in the array using Promise.all. When that is finally fulfilled, only then you can send the response. Also don't forget to send an appropriate response when an error happens.

    generateCoordinates (req, res, next) {
        var newDeals = deals.map((cur, ind, arr) =>
            new Promise((resolve, reject) => {
                geocodio.get('geocode', {
                    q: `${cur.restaurant.addressOne},
                        ${cur.restaurant.city}, ${cur.restaurant.state},
                        ${cur.restaurant.zip}`
                }, (err, response) => {
                    if (err) reject(err);
                    else resolve(response);
                });
            }).then(response => {
                var result = JSON.parse(response);
                let obj = result.results[0].location;
                cur.location = obj;
                console.log(cur)
                return cur;
            })
        );
        Promise.all(newDeals).then(results => {
            res.status(200).send(results);
        }, err => {
            res.status(500); // or whatever
            console.error(err);
        });
    }