如何在循环中调用 Promise 函数并保存其 return 值

How to call Promise function in loop and save its return value

我创建了一个名为 getBasketObject 的承诺函数(using bluebird)。此函数需要一个篮子作为参数,而不是 return 一个新的 basketObject

basketObject 有一些变量,例如 tax, total, shippingproductItems。现在,productItems 对象有 price, name, quantity 个可用属性,但没有 productImageLink 个可用属性。

为了获得 productImageLink,我对端点进行了新的异步调用,这将使我获得产品图像对象。 Image Endpoint 也作为 Promise 实现。

现在,我遍历 productLineItems 并获取 name, price, quantity 等属性的值,最后调用获取图像。

现在,如果我添加

basketObj["products"][productId]["productImageSrc"] = smallImage[0]; 我的对象从未被修改,在最终输出中我没有得到图像 link。

发生这种情况是因为在异步调用发生之前我的 getBasketObject returned 值。为了解决这个问题,我添加了 resolve(basketObj);,但是这个 return 立即退出了循环。

那么,遍历我的产品项并获取所有产品的图像 link 的正确方法是什么。

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

    });
};

如果我使用 resolve(basketObject) 我的最终对象看起来像

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

即使 productLineItems 有多个产品,您也可以看到它只获取一个产品对象

首先你的resolve(basketObj)是无效的,因为你多次为你的Promise调用resolve,但你必须只调用一次。

您还应避免将 string 用作错误,但始终使用真正的错误(不仅是承诺,而且始终在 javascript 中)。

您可以在 Promise 链中传递 Object.keys(productLineItems) 而不是 for in 循环,然后使用 .each 而不是 for in 循环。

这样你就可以 return product.getProductObject 引入的 Promise。

你可以这样重写:

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

如果不想用.each可以这样写:

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

循环未完成,因为您在第一次迭代时就解决了您的承诺。但是您需要等待 product.getProductObject 的所有异步调用完成。这里 Promise.all 提供帮助。

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

并且请确保 product.getProductObject(productId) 确实进行了异步调用