All functions in Q.all block are not being promised - javascript

I have the following code below in a then block
The issue I'm facing is at the end when i do the res.json(optionData1) its not returning the fully completed js data object i.e. the output after the processData function is missing
Am i using Q.all in the correct way?
var processUserInfo = function(categoryToProcess, inputToProcess, optionComingIn) {
var d = Q.defer();
if (optionData1['option'] == optionComingIn) {
if (optionData1[categoryToProcess].hasOwnProperty(inputToProcess)) {
optionData1[categoryToProcess][inputToProcess]++;
} else {
optionData1[categoryToProcess][inputToProcess] = 1;
}
d.resolve(optionData1);
}
}
var processData = function(item, optionComingIn) {
var d = Q.defer();
return User.find(
{_id: item},
{gender: 1, country:1},
function(req, foundUser) {
processUserInfo('gender', foundUser[0]['gender'], optionComingIn)
.then(function(resolve,reject) {
d.resolve();
});
});
return d.promise;
}
Q.all(foundQ[0]['people'].map(function(item) { // Or Q.allSettled
processCounts(item['optionSelected']);
processData(item['userID'], item['optionSelected']);
}))
.then(function(){
res.json(optionData1); //Doesnt give me the full result
});
Thanks
UPDATE: Using the return method as in the answer below got everything working.

Here is code which may work - too much "unknown" in your code snippet to be sure
modified processData to return a promise that resolves when user.Find is done
added a return in the .map, so the promise returned by processData is waited on in Q.all
So ... here's the fixed code (processuserInfo unchanged so omitted form the answer)
var processData = function (item, optionComingIn) {
// return a promise to wait for
return Q.promise(function(resolve, reject) {
User.find({
_id: item
}, {
gender: 1,
country: 1
},
function (req, foundUser) {
processUserInfo('gender', foundUser[0]['gender'], optionComingIn);
resolve();
}
);
});
}
Q.all(foundQ[0]['people'].map(function (item) { // Or Q.allSettled
processCounts(item['optionSelected']);
return processData(item['userID'], item['optionSelected']);
// return added
}))
.then(function () {
res.json(optionData1); //Doesnt give me the full result
});

Related

Figuring the complexity of Promise.all

I have been struggling for quite some time to get this multiple async nodejs request apis to work but unfortunately i am not able to get them work.
Index.js Code:
service.get(
"/restraunts",
versionRoutes({
"1.0.0": getRestrauntsList
})
);
function getRestrauntsList(req, res, next) {
console.log("Started getRestrauntsList");
file1
.appEnvironment(req, res, next)
.then(function(result) {
return file2.getRestrauntsList(req, res, next);
})
.then(function(result) {
res.status(200).send(result);
return;
})
.catch(function(errorResult) {
res.status(500).send(errorResult);
return;
});
}
File2.js
module.exports = {
getRestrauntsList: function(req, res, next) {
console.log("getRestrauntsList started..");
var cities = [1, 2, 3, 4, 5];
let restrauntsList = [];
let urlArray = [];
var restrauntsListPromise = cities.map(function(id) {
return new Promise(function(resolve, reject) {
var options = {
method: "GET",
url: "someurl/" + id + "/restaurants",
headers: {
"AUTH-TOKEN": "TOKEN"
}
};
request(options, function(error, response, body) {
if (error) {
if ("message" in error) {
errorMsg = error.message;
var result = {
status: "error",
message: errorMsg
};
} else {
var result = {
status: "error",
message: "Resource Timeout."
};
}
reject(result);
return promise;
}
console.log(
"Response: " + JSON.stringify(response)
);
if (response.statusCode === 200 || response.statusCode === 201) {
body = JSON.parse(body);
if (body.success) {
let result = {
status: "success",
data: body.result
};
resolve(result);
} else {
let result = {
status: "error",
message: body.error
};
reject(result);
}
} else {
let result = {
status: "error",
message: body.error
};
reject(result);
}
});
});
});
console.log('restrauntsListPromise:' + JSON.stringify(restrauntsListPromise));
Promise.all(restrauntsListPromise).then(function(result) {
var content = result.map(function(restraunts) {
return restrauntsList.push(restraunts.body);
});
// res.send(content);
resolve({
restrauntsList: restrauntsList
});
return promise;
});
},
};
Ideally i expect to get the response of all the apis in the
restrauntsListPromise
and then using Promise.all i should iterate all the promises and formulate my required object.
The response of my code however is
restrauntsListPromise:[{},{},{},{},{}]
and then
Response: {"statusCode":200,"body":"{\"success\":true,\"res
Response: {"statusCode":200,"body":"{\"success\":true,\"res
Response: {"statusCode":200,"body":"{\"success\":true,\"res
Response: {"statusCode":200,"body":"{\"success\":true,\"res
Response: {"statusCode":200,"body":"{\"success\":true,\"res
Ideally what should happen is i should be able to pass the combined result of all the five apis calls as a single object back to the calling promise here
.then(function(result) {
res.status(200).send(result);
return;
})
The problem being the method getRestrauntsList finishes execution and then after some time, i get the responses of the apis.
The problem being the method getRestrauntsList finishes execution and then after some time, i get the responses of the apis.
This is because you're not returning a promise from the getRestrauntsList().
There are few items that needs to addressed to make it work
1. Remove the unused variables
return promise; // both inside promise.all[] and request()
There is no declared variable named promise. So, you can remove it.
2. Accessing .body instead of .data
You're resolving as resolve({status: "success", data: body.result}); But When you are iterating, you are accessing using .body instead of .data. You need to be using .data. Also, you can eliminate restrauntsList array since you're using a .map()
3. Calling resolve() to return values.
You can't use resolve() to return value within Promise.all[] since you didn't create a promise using new Promise((resolve, reject) => { ... });. By default, a return within a promise will be a promise. so, a simple return will suffice. But if you want to be explicit, you can also return using Promise.resolve()
Making those changes,
return Promise.all(restrauntsListPromise).then(function (result) {
return {
restrauntsList: result.map(function (restraunts) {
return restraunts.data;
})
};
//or using Promise.resolve();
// return Promise.resolve({
// restrauntsList: result.map(function (restraunts) {
// return restraunts.data;
// })
// });
});
You are looking for
return Promise.all(restrauntsListPromise).then(function(result) { /*
^^^^^^ */
var contents = result.map(function(restaurants) {
return restaurants.body;
// ^^^^^^^^^^^^^^^^^^^^^^^^
});
return {restaurantsList: contents};
// ^^^^^^
});
You need to return the promise chain from the getRestrauntsList method, you should return the value from the map callback instead of using push on an array, and you will need to return from the then callback - there is no resolve function as you're not inside a new Promise constructor that you only need for callback APIs.

Promises in JavaScript not executing propertly

I have this series of steps that needs to be completed in order:
Validate an object
Fetch an image URL using Bing's image search API
Add the found URLs to the object
Make a post request and send the object
A sample object looks like:
`{
options: [{
text: 'Pizza',
votes: 0,
imageURL: ""
}, {
text: 'Hot Dog',
votes: 0,
imageURL: ""
}]
};`
Because the order in this series, I am using promises to make sure everything goes in the order specified above. So far I have:
function validatePoll() {
var isValid = true;
for (var i = 0; i < $scope.poll.options.length; i++) {
if (!$scope.poll.options[i].text) {
isValid = false;
break;
}
}
return isValid;
}
let promiseURL = function(searchTerm) {
return new Promise(function(resolve, reject) {
$http.get('https://api.cognitive.microsoft.com/bing/v5.0/images/search?q=' + searchTerm + '&count=1&offset=0&mkt=en-us&safeSearch=Strict', {
headers: {
'Ocp-Apim-Subscription-Key': 'XXXXXXXXXXXXXXXXXXXXXXXXXX'
}
}).success(function(response) {
console.log(response);
resolve(response);
}).error(function (err, status) {
reject(err);
})
})
};
let fetchImageURL = function() {
for(var i = 0; i < $scope.poll.options.length; i++) {
console.log(promiseURL($scope.poll.options[i].text));
}
}
$scope.submitChoice = function() {
var isValid = validatePoll();
if(isValid) {
fetchImageURL();
} else {
console.log("Not Valid Poll");
}
}
But what ends up happening is the console.log(promiseURL($scope.poll.options[i].text)); in the fetchImageURL returns an unresolved promise instead of the response string I want instead. How might I be able to fix the code to ensure that:
A call to promiseURL is made with the proper argument
A response is received and can be parsed
The parsed information can be added to the imageURL property in the polls object
You're seeing the printout of the Promise because you're printing out the promise. You need to be handling the promise resolve/reject. What you should be calling is:
promiseURL($scope.poll.options[i].text)
.then(function(data){
console.log("Success",data)
})
.catch(function(error){
console.log("Error",error")
})
Promises are usually used as follows:
promise.then(function(result) {
console.log(result); // "Stuff worked!"
}, function(err) {
console.log(err); // Error: "It broke"
});
Where the promise itself looks like:
var promise = new Promise(function(resolve, reject) {
// do a thing, possibly async, then…
if (/* everything turned out fine */) {
resolve("Stuff worked!");
}
else {
reject(Error("It broke"));
}
});
It doesn't look like, in your loop, you're calling then on promiseURL($scope.poll.options[i].text). Try doing something like:
for(var i = 0; i < $scope.poll.options.length; i++) {
promiseURL($scope.poll.options[i].text).then(function(result) {
console.log(result); // "Stuff worked!"
});
}
See https://developers.google.com/web/fundamentals/getting-started/primers/promises for a great primer on promises.

Javascript - Promises and forEach

I am trying to fill the array with avatarIcon node from parsed xmls but my final array in res.view is empty plus the execution doesn't seem to reach return res.view function. How do I do this correctly?
function parseXML(xml) {
var parsed
parseString(xml, { explicitArray: false }, function(err, result) {
parsed = result
})
return parsed
}
function findUsers() {
return new Promise(function(resolve, reject) {
User.find().exec(function(err, sids) {
resolve(sids)
})
})
}
avatars: function(req, res) {
var arr = []
findUsers().then(function(result) {
result.forEach(function(el) {
getProfileXML(el.sid).then(function(result) {
arr.push(parseXML(result).profile.avatarIcon)
})
})
return res.view('users', {
users: arr
})
})
}
You can use Promise.all to wrap a collection of promises. Here untested code to demonstrate the use, where the result of the expression is a promise too:
return findUsers().then(function(result) {
var promises = result.map(function(el) {
return getProfileXML(el.sid);
});
return Promise.all(promises);
}).then(function(values) {
var arr = values.map(function(v) {
return parseXML(v).profile.avatarIcon;
});
return res.view('users', {
users: arr
})
})

Promise.promisify is not a function

I wrote JavaScript like this:
var keys=null;
var promise=Promise.promisify(alchemyapi.keywords("url",myUrl,{},function(response) {
var keywords = { url:myUrl, response:JSON.stringify(response,null,4), results:response['keywords'] };
return keywords;
}));
promise.then(
(result)=>{
var keys=result;
console.log(keys);
},
(error)=>console.log(error)
);
I'm using AlchemyAPI and trying to store data I got into my database
How should I do?
You should be able to use Promise to return expected results by removing .promisify which is not a built-in Promise method ; substituting passing keywords to resolve within Promise constructor for return
var keys = null
, promise = new Promise(function(resolve) {
alchemyapi.keywords("url", myUrl, {}, function(response) {
var keywords = {url: myUrl
, response: JSON.stringify(response,null,4)
, results:response['keywords']
};
resolve(keywords);
// error handling ?
})
}).then(function(result) {
keys = result;
console.log(keys)
}, function(err) {
console.log(err)
})
For a more general Promise.promisify function without Bluebird, I ended up writing this:
function promisify(func) {
return function promiseFunc(options) {
return new Promise(function executor(resolve, reject) {
func(options, function cb(err, val) {
if (err) {
return reject(err);
} else {
return resolve(val);
}
});
});
}
}
Hopefully someone else finds this helpful, but in most cases it's probably worth importing Bluebird.

Javascript Promise resolve value

I'm trying to return a value from a promise, but I can't. Why it doesn't work?
I get Promise { <pending> } output only.
function
function username() {
return new Promise(function (resolve, reject) {
user.where('id', req.id).fetch().then(function (data) {
data = data.toJSON();
resolve(data);
});
});
}
variable
var r = username().then(function (a) {
return a.username;
});
console.log(r);
If I remove return and put console.log(a.username), it works, but it's not the result I want. I want to put returned value inside r.
EDIT #1
I need to pass my returned values into a view (like below), so I must be able to access them outside of the then() chain.
res.render("frontend/index", {
value1 : value1,
value2 : value2,
value3 : value3
});
EDIT #2
I'm using Express.js
Now I get "Error: Can't set headers after they are sent." error, I hope it's more clear now. When a user tries to access a page, I query the database and pass variables to a view, but there are more than one operation per view (username, post info, comments, etc).
exports.index = function (req, res) {
function username() {
return new Promise(function (resolve, reject) {
user.where('id', req.id).fetch().then(function (data) {
data = data.toJSON();
resolve(data);
});
});
}
username().then(function (b) {
res.render('backend/index', {
username: b.username
});
});
function post() {
return new Promise(function (resolve, reject) {
post.where('id', req.params.postId).fetch().then(function (data) {
data = data.toJSON();
resolve(data);
});
});
}
post().then(function (a) {
res.render('backend/index', {
post: a.post
});
});
};
The .then() function returns a promise only. Here in above code variable r is nothing but a promise reference object.
If you want to use the returned response from the promise, this is how you will do it -
username().then(function (a) {
console.log(JSON.stringify(a));
// you will need to use the response returned from server here..
// display in view or something else
res.render("frontend/index", {
value1 : a.value1,
value2 : a.value2,
value3 : a.value3
});
});
Returning a value from inside a promise thenable function will only return a promise and not the value.
You will need to wait till all the promises are resolved and then only send the response from the server. Once a response is sent, you can not send it again and hence the error headers....
Updated answer as per modified question -
exports.index = function (req, res) {
function username() {
return new Promise(function (resolve, reject) {
user.where('id', req.id).fetch().then(function (data) {
data = data.toJSON();
resolve(data);
});
});
}
function post() {
return new Promise(function (resolve, reject) {
post.where('id', req.params.postId).fetch().then(function (data) {
data = data.toJSON();
resolve(data);
});
});
}
username().then(function (b) {
post().then(function (a) {
res.render('backend/index', {
post: a.post,
username: b.username
});
});
});
};
Try performing next process where fulfilled value of r is used inside of .then() to access asynchronous returned value from Promise r
var r = username().then(function (a) {
return a.username;
});
r.then(function(data) {
// `data` : `r` return value
// do stuff with `data` here
});
You'll have to restrict the usage of the value to functions passed to .then. There is no other logical way to assert that the value of r has been assigned.
var r;
username().then(function(a) {
r = a.username;
// use r here
// resolve promise with value of username
return r;
}).then(function(username) {
// or use it here
});

Categories