2016-04-09 2 views
0

Я создал функцию обещания (using bluebird) под названием getBasketObject. Эта функция ожидает корзину в качестве аргумента, а не возвращает новый basketObject.Как вызвать функцию Promise в цикле и сохранить возвращаемое значение

basketObject имеет некоторые переменные, такие как tax, total, shipping и productItems. Теперь объект productItems имеет price, name, quantity свойств, доступных в нем, но у него нет productImageLink.

Для получения productImageLink Я делаю новый асинхронный вызов к конечной точке, которая доставит мне объект изображений продукта. Image Endpoint также реализуется как обещание.

Теперь я перехожу через productLineItems и получаю значение атрибутов, например name, price, quantity, и, наконец, сделаю звонок, чтобы получить изображение.

Теперь, если добавить

basketObj["products"][productId]["productImageSrc"] = smallImage[0]; мой объект никогда не модифицирована и в конечном выходе я не получаю ссылку на изображение.

Это происходит потому, что мое getBasketObject вернуло значение перед асинхронным вызовом. Чтобы решить эту проблему, я добавил resolve(basketObj);, но это немедленно возвращается, и я вышел из цикла.

Итак, каков правильный способ перебора элементов продукта и получения ссылок на изображения для всех продуктов.

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 имеет несколько продуктов

+0

ли 'productLineItems' массив или объект? Использование цикла 'for in' указывает, что это не массив, а потому, что вы называете его' index', а не 'key' выглядит так, как будто это массив. –

+0

@ t.niese: Его объект, я исправил его, чтобы использовать «ключ», чтобы избежать путаницы. Спасибо – RanRag

ответ

1

Прежде всего ваш resolve(basketObj) не действителен потому что вы звоните resolve несколько раз для вас Promise, но вы должны называть его только один раз.

Вы также должны избегать использования string в качестве ошибок, но всегда используйте реальную ошибку (не только с обещаниями, но и все время в javascript).

Вместо вашего for in петли вы можете передать Object.keys(productLineItems) в вашей Promise цепи, а затем использовать .each вместо петли for in.

Таким образом, вы можете вернуть обещание, введенное product.getProductObject.

Вы можете переписать так:

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

'compatObj.each' не входит в стандартный ES6' Promise' объект. И еще один недостаток, заключающийся в том, что '.each' выполняет обещания в порядке последовательности, а не параллельно, например' Promise.all' do –

+0

@NickRassadin OP утверждает, что используется «bluebird», но да, это может быть написано по-разному. –

+0

Отлично! Лот, чтобы учиться в обещаниях :) – RanRag

1

Loop не закончил, потому что вы решить свои обещания на первой итерации. Но вам нужно дождаться всех асинхронных вызовов 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) делает действительно сделать асинхронной называет

+0

Если вы вызываете 'resolve' в обратном вызове' then', вы, скорее всего, используете Promise неправильным способом. Это не означает, что ваше решение не работает, просто вы не должны использовать Promise таким образом. Это похоже на «Забытое обещание» (http://taoofcode.net/promise-anti-patterns/#the-forgotten-promise:8f173b15e2d19515fdc8ce931ae539c0) –

+0

Нет, вы ошибаетесь. Здесь метод return обещает, что все параллельные обещания закончатся. Вы не можете сделать иначе. Это далеко от шаблона «Забытые обещания». –

+0

Шаблон 'return new Promise (функция (разрешить, отклонить) {/*...*/ Promise.all (asyncCalls). Then (function (value) {resolve (basketObj);}, function (reason) {reject (reject) причина);}) '- плохой шаблон, и это то, что вы написали в своем ответе. Либо« возврат нового обещания »не требуется, либо что-то до того, как« Promise.all »ошибается. И поэтому он аналогичен «Забытое обещание». –

Смежные вопросы