Reputation: 113
I am having trouble figuring out how to accomplish what I am trying given the asynchronous nature of Node. On a conceptual level I want to do this:
Here is the code I have:
client.get('search/tweets', {q: 'node.js'}, function(error, tweets, response)
{
for (var i=0; i<tweets.statuses.length; i++){
var ttext = tweets.statuses[i].text;
var tsrc = tweets.statuses[i].source;
var lang = '';
var sentiment = '';
var score = '';
var tweet = {
text: tweets.statuses[i].text,
source: tweets.statuses[i].source
};
alchemyapi.sentiment("text", ttext, {}, function(response) {
if (response.status == 'OK' ){
tweet.lang = response.language;
tweet.sentiment = response['docSentiment'].type;
tweet.score = response['docSentiment'].score;
console.log("pushing: " + util.inspect(tweet) );
tweet_data.push( tweet );
}
});
}
res.render('twitter', {
myText: myText,
});
});
What's happening is the page is being rendered before any of the alchemyapi calls have completed. I am fairly new to thinking in async terms and haven't wrapped my head fully around it yet.
Any help would be greatly appreciated!
Thanks, Andy
Edit -
I am trying to implement the deferred approach using Promises and not having much luck.. I have parseTweet defined now as a function as you suggested, if the response is 'OK' it calls resolve(tweet) - not really sure if this is doing anything. My for() loop now looks like this:
promises.push(parseTweet(tweets.statuses[i]));
Promise.all(promises).then(function(){
console.log('all promises complete');
}, function(){
console.log('error occurred with promises');
});
I have comments in the parseTweet function and I see all of the tweets being parsed but not inside the Promise.all block - and the program never completes, keeps running until I kill it...
Upvotes: 0
Views: 99
Reputation: 2799
Ok, function "alchemyapi.sentiment" is async and you are calling a lot of times, exactly "tweets.statuses.length", and, at the end of your code you are calling function "res.send" so you call this function before "for loop" end, so, this is my approach to the solution (pseudocode):
1) Create a function that receive a tweet, create the tweet data (your logic + alchemyapi.sentiment). This function return a javascript promise (in my example I will use jquery defer to write less code, but you can use native promise):
function parseTweet(tweet) {
$promise = $.defer();
var tweetDataObject = {}; // this is not important, put your required data
// call alchemyapi async
alchemyapi.sentiment("text", ttext, {}, function(response) {
if (response.status == 'OK' ) {
// parse data and "return" valid object
$promise.resolve(tweetDataObject);
} else { // data is not valid, return null
$promise.reject(null); // null, empty object, what you need
}
});
return $promise;
}
2) On your main method, do the for loop, call function parseTweet (that return something like promise) and wait for ALL promise:
var promises = [];
for (var i=0; i<tweets.statuses.length; i++) {
promises.push(parseTweet(tweets.statuses[i]));
$.when(promises).then(function() {
// here, on "arguments" you have array of objects that parseTweet "return" when called resolve or reject
console.log(arguments);
});
}
I wrote the example using jquery defer, but it's easy with native promises (specially at this case that we have a lot number of promises to wait on $.when function).
Please, read carefully this article: http://www.html5rocks.com/en/tutorials/es6/promises/
I did an example on github. If you have doubts let me know, but I think that it's very clear https://github.com/josemato/stackoverflow/tree/master/es-promise-all-no-order
Upvotes: 1
Reputation: 308753
It'd be easy with vert.x: when the request event comes in, defer it to a worker module and let it do the long running processing. Register a handler that processes the "all finished" event for display.
Maybe that process works for node.js as well: Make the long running alchemy calls a separate thread that registers an "all finished" event when it's done. Ask the UI to register itself with the callback to render when it receives that event.
Upvotes: 0