Edmund Edmund - 26 days ago 14
Javascript Question

Node + ES6: How to use Promise.all with async requests?

I have a method called

fetchMerchantData
which calls 3 other async methods. I'm trying to use
Promise
so that it doesn't call
resp.direct(301, ...)
until all the requests are finished but it's not working.

function fetchOauth2Token(authorizationCode, resp) {
...
request({
url: `https://connect.squareup.com/oauth2/token`,
method: "POST",
json: true,
headers: oauthRequestHeaders,
form: oauthRequestBody,
}, (error, oauthResp, body) => {
if (body.access_token) {
Promise.resolve(fetchMerchantData(body.access_token, body.merchant_id)).then(() => {
console.log("last!"); //<--------------------- this is printing first
resp.redirect(
301,
`myurl.com/blah`
);
});
;
} else {
// TODO find out what to do on failure
resp.redirect(301, `myurl.com/?error=true`);
}
})
}

function fetchMerchantData(access_token, merchant_id){
const updates = {};
request({
url: `https://connect.squareup.com/v1/me/locations`,
method: "GET",
json: true,
headers: {
Authorization: `Bearer ${access_token}`,
Accept: 'application/json',
"Content-Type": "application/json",
},
}, (error, response) => {
if (!error) {
const locations = response.body;

Promise.all([
saveMerchant(merchant_id, access_token, locations),
saveLocations(merchant_id, locations),
installWebhookForLocations(access_token, locations),
]).then(values => {
console.log("first!"); //<--------------------- this is printing last
return Promise.resolve("Success");
})
}
});
}


And here's an example of the
saveMerchant
method which calls firebase:

function saveMerchant(merchant_id, access_token, locations) {
const merchantRef = database.ref('merchants').child(merchant_id);
const location_ids = locations.map(location => location.id);

merchantRef.update({
access_token,
location_ids,
});
}


How would I synchronize this?

== UPDATE ==

This is how my
installWebhookForLocations
method looks:

function installWebhookForLocations(access_token, locations){
const locationIds = locations.map(location => location.id);
locationIds.forEach((locationId) => {
request({
url: `https://connect.squareup.com/v1/${locationId}/webhooks`,
method: "PUT",
json: true,
headers: {
Authorization: `Bearer ${access_token}`,
Accept: 'application/json',
"Content-Type": "application/json",
},
body: ["PAYMENT_UPDATED"],
}, (error) => {
if (!error){
console.log(`Webhook installed for ${locationId}`);
}
});
});
}

Answer

Here is an example of saveMerchant that would use a promise.

function saveMerchant(merchant_id, access_token, locations) {
  return new Promise(function (resolve, reject) {
    const merchantRef = database.ref('merchants').child(merchant_id);
    const location_ids = locations.map(location => location.id);

    merchantRef.update({
      access_token,
      location_ids,
    }, function (error) {
      if (error) return reject(error);
      resolve(); 
    });
  });
}

To make the above easier, there is a nice Promise library called Bluebird, it has a promisify utility, that you could apply to firebird update method.

Also for your second question were your using forEach, bluebird has a nice utility function called map that you could use instead.

Comments