HMS
HMS

Reputation: 157

Making promise wait for another promise

Within a node.js app, I'm trying to look up a list of products from an API, then get the thumbnail image for the first item only.

Within my code getOrderDetails is a promise, and then if it's the first product, it calls function getImage which is also a promise.

However when I run the code the orderDetails is populated without the thumbnail - so it looks like the code isn't waiting for the getImage function to resolve.

Please can someone explain what I have done wrong?

Thank you in advance!

if (siteConfig.storeIntegration == 'Cart') {

    getOrderDetails(restPathOrder)
    .then(function(res) {

        var orderDetails = '';

        for (i = 0; i < res.items.length; i++) {

            productId = res.items[i]['productId'];

            thumbnail = '';

            if (i == 0) {

                getImage(productId, restApiKey)
                .then(function(res) {

                    thumbnail = res;

                    console.log('thumbnail:' + res);
                })
            }

            orderDetails = orderDetails + res.items[i]['quantity'] + ',' + res.items[i]['name'] + ',' + productUrl + ',' + thumbnail;

        }

        console.log(orderDetails);

    })


}

function getOrderDetails(restPath) {

    return new Promise((resolve, reject) => {

        request(restPath, function (error, response, body) {

            if (!error && response.statusCode == 200) {

                restData = JSON.parse(body);

                resolve(restData)
             }
        })
    })


}

function getImage(productId, restApiKey) {

    return new Promise((resolve, reject) => {

        var restPathProduct = storeURL + restApiKey + productId;

        request(restPathProduct, function (error, response, body) {

            if (!error && response.statusCode == 200) {

                restData = JSON.parse(body);

                thumbnail = restData.image[0];

                resolve(thumbnail);
             }
        })
    })

}

Upvotes: 0

Views: 284

Answers (2)

ZeroBased_IX
ZeroBased_IX

Reputation: 2727

@FrankerZ is right that async/await makes the code much cleaner.

I'm unsure on your business logic, where productUrl comes from.

The solution is to wrap the whole block of code in a promise, get the image for the first order, and in the then of getImage, then you can process the order details.

Here is my attempt, with some minor refactors. Hopefully it can steer you in the right direction.

function process() {
  // Wrap everything inside a promise, we then resolve with the order details
  // Resolve is what gets returned in the .then((resolvedData)), reject is what gets returned in the .catch((rejectedThings))
  return new Promise((resolve, reject) => {

    // Get the order details
    getOrderDetails(restPathOrder)
      .then(function(orderRes) {

        // Handle no items
        if (!orderRes.items || orderRes.items.length === 0) {
          return reject("No items") // You can deal with this in the catch
        }
        // Get first item 
        const firstOrder = orderRes.items[0];

        // Fetch the first thumbnail then proceed to fetch the rest of the order details. 
        getImage(firstOrder.productId, restApiKey)
          .then(function(thumbnail) {

            let orderDetails = '';
            // Build order details body
            // N.B I'm not sure how you want orderDetails to appear, personally I would use a map instead of a forEach. 
            orderRes.items.forEach((item) => {
              orderDetails = orderDetails + item.quantity + ',' + item.name + ',' + productUrl + ',' + thumbnail;
            })

            // Then resolve with the order details
            return resolve(orderDetails);
          })
      })
  })
}

// You can then call process as a promise with the order details
process().then(function(orderDetails) => console.log(orderDetails))

Upvotes: 1

Blue
Blue

Reputation: 22911

Might I suggest using new await syntax?

async main() {
    if (siteConfig.storeIntegration == 'Cart') {
        let res = await getOrderDetails(restPathOrder);

        var orderDetails = '';

        for (i = 0; i < res.items.length; i++) {

            productId = res.items[i]['productId'];

            let thumbnail = '';

            if (i == 0) {
                thumbnail = await getImage(productId, restApiKey);
            }

            orderDetails += res.items[i]['quantity'] + ',' + res.items[i]['name'] + ',' + productUrl + ',' + thumbnail;
        }
        console.log(orderDetails);
    }
}

Upvotes: 1

Related Questions