Reputation: 157
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
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
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