2016-04-09 3 views
0

Ich habe eine Promise-Funktion (using bluebird) namens getBasketObject erstellt. Diese Funktion erwartet einen Korb als Argument und gibt dann einen neuen basketObject daraus zurück.So rufen Sie Promise-Funktion in Schleife und speichern Sie den Rückgabewert

basketObject hat einige Variablen wie tax, total, shipping und productItems. Das Objekt productItems verfügt jetzt über price, name, quantity verfügbare Eigenschaften, aber productImageLink ist nicht verfügbar.

Um productImageLink zu bekommen, mache ich einen neuen Async-Aufruf an einen Endpunkt, der mir das Produktbilder-Objekt holt. Image Endpoint ist auch als ein Versprechen implementiert.

Nun, ich Schleife über die productLineItems und erhalten Sie den Wert von Attributen wie name, price, quantity und schließlich den Aufruf zum Bild zu machen.

Nun, wenn ich

basketObj["products"][productId]["productImageSrc"] = smallImage[0]; mein Objekt hinzufügen, wird nie geändert und in der endgültigen Ausgabe ich nicht Bild Link.

Dies passiert, weil mein getBasketObject Wert zurückgegeben, bevor der Async-Aufruf aufgetreten ist. Um dies anzugehen, habe ich resolve(basketObj); hinzugefügt, aber das kehrt sofort zurück und bin aus der Schleife.

Also, was ist der richtige Weg, um meine Produkt-Artikel zu durchlaufen und Bild-Links für alle Produkte zu erhalten.

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

    }); 
}; 

Wenn ich resolve(basketObject) mein letztes Objekt verwenden sieht aus wie

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

Sie sehen es auch nur ein einziges Produkt Objekt erhält, wenn productLineItems mehrere Produkte

+0

Ist 'productLineItems' oder ein Objekt macht? Die Verwendung einer 'for in'-Schleife zeigt an, dass es sich nicht um ein Array handelt, aber weil Sie es' index' nennen und nicht 'key', sieht es so aus, als wäre es ein Array. –

+0

@ t.niese: Es ist ein Objekt, ich habe es korrigiert, um Schlüssel zu verwenden, um Verwirrung zu vermeiden. Danke – RanRag

Antwort

1

Zu allererst Ihren resolve(basketObj) ist nicht gültig ist weil du resolve mehrfach für dich anrufst, aber du musst es nur einmal anrufen.

Sie sollten auch vermeiden, string als Fehler zu verwenden, aber immer echten Fehler verwenden (nicht nur mit Versprechen, sondern die ganze Zeit in Javascript).

Statt Ihre for in Schleife können Sie Object.keys(productLineItems) in Ihrem Versprechen Kette, übergeben und dann die .each anstelle der for in Schleife.

Auf diese Weise können Sie das Versprechen von product.getProductObject zurückgeben.

Man könnte es so umschreiben:

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

Wenn Sie nicht .each verwenden möchten Sie es auf diese Weise schreiben:

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

'promiseObj.each' ist nicht Teil des Standard ES6' Promise' Objektes. Und ein weiterer Nachteil, dass '.each' die Versprechen in der Reihenfolge, nicht parallel, wie' Promise.all' laufen lässt, tun –

+0

@NickRassadin der OP besagt, dass 'bluebird' verwendet wird, aber ja könnte es anders geschrieben werden. –

+0

Perfekt! Lot in Versprechen zu lernen :) – RanRag

1

Schleife nicht beendet haben, weil Sie Ihre Versprechen lösen bei der ersten Iteration. Aber Sie müssen alle asynchronen Anrufe von product.getProductObject bis zum Ende warten. Hier Promise.all zu helfen.

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

Und stellen Sie sicher, product.getProductObject(productId) wirklich ein Array async nennt

+0

Wenn Sie 'resolve' innerhalb des Callbacks eines' then' aufrufen, verwenden Sie höchstwahrscheinlich die falsche Methode. Dies bedeutet nicht, dass Ihre Lösung nicht funktioniert, nur dass Sie Promise nicht auf diese Weise verwenden sollten. Es ist ähnlich wie die [ 'The Forgotten Promise'] (http://taoofcode.net/promise-anti-patterns/#the-forgotten-promise:8f173b15e2d19515fdc8ce931ae539c0) –

+0

Nein, du irrst. Hier verspricht die Methodenrückgabe, dass alle konkurrierenden Versprechen abgeschlossen werden. Das kann man nicht anders machen. Dies ist weit entfernt von The Forgotten Promise Muster. –

+0

Das Muster 'return new Promise (Funktion (auflösen, ablehnen) {/*...*/ Promise.all (asyncCalls) .then (Funktion (Wert) {auflösen (basketObj);}, Funktion (Grund) {ablehnen (Grund);}) 'ist ein schlechtes Muster, und das ist, was Sie in Ihrer Antwort geschrieben haben. Entweder die' Rückkehr neue Verheißung' ist nicht erforderlich, oder etwas vor der 'Verheißung.all' ist falsch. Und deshalb ist es ähnlich Das "The Forgotten Promise". –

Verwandte Themen