How to call the Promise function in a loop and save the return value

I created a promise function (using bluebird) called getBasketObject . This function expects the basket as an argument and returns basketObject from it.

basketObject has some variables like tax, total, shipping and productItems . Now the productItems object has price, name, quantity properties available in it, but it does not have productImageLink .

To get productImageLink , I make a new asynchronous call to the endpoint, which will deliver me the product image object. Image Endpoint also being implemented as a promise.

Now I productLineItems over the productLineItems and get the value of attributes like name, price, quantity , and finally make a call to get the image.

Now if i add

basketObj["products"][productId]["productImageSrc"] = smallImage[0]; my object never changes, and in the end result, I don't get a link to the image.

This is because my return value getBasketObject before calling the asynchronous call. To solve this problem, I added resolve(basketObj); but it immediately returns and exits the loop.

So what is the right way to iterate over product elements and get image links for all products.

 exports.getBasketObject = function(basket) { return new Promise(function(resolve, reject){ if (!basket){ reject("Please give valid basket"); } var basketObj = {}; if ('order_total' in basket && basket.order_total) { basketObj.total = basket.order_total; } else if ('product_total' in basket && basket.product_total) { basketObj.total = basket.product_total; } var productLineItems = basket.product_items; basketObj["products"] = {}; for (var key in productLineItems) { var productItem = productLineItems[key]; var productId = productItem.product_id; //Async call to get Product Object product.getProductObject(productId).then(function(productObj){ basketObj["products"][productId] = {}; basketObj["products"][productId]['productQuantity'] = productItem.quantity; basketObj["products"][productId]["productName"] = productItem.item_text; basketObj["products"][productId]["productPrice"] = productItem.base_price; //If promise resolved, get images var imageObject = product.getProductImages(productObj[0]); var smallImage = imageObject['small']; basketObj["products"][productId]["productImageSrc"] = smallImage[0]; resolve(basketObj); //Acts as a return }); } }); }; 

If I use resolve(basketObject) , my final object looks like

  { "total": 95.99, "tax": "N/A", "shipping": "N/A", "products": { "701642890706": { "productQuantity": 1, "productName": "Novelty Stitch Belted Cardigan", "productPrice": 95.99, "productImageSrc": "image.png" } } } 

You can see that it receives only one product object, even if productLineItems has several products

0
source share
2 answers

First of all, your resolve(basketObj) invalid, because you call resolve several times for you to Promise, but you should only call it once.

You should also avoid using string as errors, but always use a real error (not only with promises, but all the time in javascript).

Instead of your for in loop, you can pass Object.keys(productLineItems) in the promise chain and then use .each instead of the for in loop.

This way you can return the promise entered by product.getProductObject .

You can rewrite it like this:

 exports.getBasketObject = function(basket) { var basketObj = {}; var productLineItems; return Promise.resolve(basket) .then(function(basket) { if( !basket ) { throw new Error("Please give valid basket"); } productLineItems = basket.product_items; }) .then(function() { if ( 'order_total' in basket && basket.order_total) { basketObj.total = basket.order_total; } else if ( 'product_total' in basket && basket.product_total) { basketObj.total = basket.product_total; } basketObj.products = {}; //return the all keys of the productLineItems to be able to iterate over it using promises return Object.keys(productLineItems); }) .each(function(key) { var productItem = productLineItems[key]; var productId = productItem.product_id; basketObj.products[productId] = {}; basketObj.products[productId].productQuantity = productItem.quantity; basketObj.products[productId].productName = productItem.item_text; basketObj.products[productId].productPrice = productItem.base_price; //Async call to get Product Object return product.getProductObject(productId).then(function(productObj) { //If promise resolved, get images var imageObject = product.getProductImages(productObj[0]); var smallImage = imageObject.small; basketObj.products[productId].productImageSrc = smallImage[0]; }); }) .then(function() { // return the basketObj after all product.getProductObject resolved return basketObj; }); }; 

If you do not want to use .each , you can write it like this:

 exports.getBasketObject = function(basket) { var basketObj = {}; var productLineItems; return Promise.resolve(basket) .then(function(basket) { if( !basket ) { throw new Error("Please give valid basket"); } productLineItems = basket.product_items; }) .then(function() { if ( 'order_total' in basket && basket.order_total) { basketObj.total = basket.order_total; } else if ( 'product_total' in basket && basket.product_total) { basketObj.total = basket.product_total; } basketObj.products = {}; var promises = []; Object.keys(productLineItems).forEach(function(key) { var productItem = productLineItems[key]; var productId = productItem.product_id; basketObj.products[productId] = {}; basketObj.products[productId].productQuantity = productItem.quantity; basketObj.products[productId].productName = productItem.item_text; basketObj.products[productId].productPrice = productItem.base_price; promises.push( product.getProductObject(productId).then(function(productObj) { //If promise resolved, get images var imageObject = product.getProductImages(productObj[0]); var smallImage = imageObject.small; basketObj.products[productId].productImageSrc = smallImage[0]; }); ); }); return Promise.all(promises); }) .then(function() { return basketObj; }); }; 
+1
source

Loop did not finish because you solved your promise in the first iteration. But you need to wait for all asynchronous calls to product.getProductObject . Here is Promise.all to help.

 ... var asycnCalls = []; for (var index in productLineItems) { ... //Async call to get Product Object asyncCalls.push( product.getProductObject(productId).then(function(productObj){ //If promise resolved, get images var imageObject = product.getProductImages(productObj[0]); var smallImage = imageObject['small']; basketObj["products"][productId]["productImageSrc"] = smallImage[0]; }) ) } //end of for loop Promise.all(asyncCalls).then(function(value) { resolve(basketObj); //Acts as a return }, function(reason) { reject(reason); }); 

And make sure that product.getProductObject(productId) does make asynchronous calls

+1
source

Source: https://habr.com/ru/post/1273433/


All Articles