bigpotato
bigpotato

Reputation: 27517

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}`);
      }
    });
  });
}

Upvotes: 1

Views: 102

Answers (1)

Keith
Keith

Reputation: 24191

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.

Upvotes: 1

Related Questions