var userName = 'realgrumpycat';
var moreAvailable = true;
var lastId = '';
while (moreAvailable)
{
getPhotosDataFromRequest(userName, lastId).then(function (data)
{
moreAvailable = data.more_available;
lastId = data[data.length - 1].id;
console.log(data);
});
}
getPhotosDataFromRequest() returns new Promise() and JSON with data. I'd like to execute this method several times at cyscle. But as I see at debugger, while loop executes so fast, that doesn't step into promise then block
Try using function recursion:
var userName = 'realgrumpycat';
var lastId = '';
var getPhotos = function()
{
getPhotosDataFromRequest(userName, lastId).then(function (data)
{
lastId = data[data.length - 1].id;
console.log(data);
if (data.more_available)
{
getPhotos();
}
});
};
getPhotos();
just as iterative alternative (as concept), but not really a solution in real life, because of performance and limits:
//i try to use here es5 only
var userName = 'realgrumpycat';
var moreAvailable = true;//<-- can be removed
var lastId = ''; //<-- can be removed
var maxRequests = 1000; //e.g. max 1000 requests
//create empty promise to resolve later
var resolveStart = null;
var request = new Promise(function(resolve){
resolveStart = resolve;
});
//append 1000 potential requests
for(var i = 0; i < maxRequests; i++) {
request = request.then(createRequestPromise);
}
//here you probably should differ the "done" rejection and other errors
request.catch(function(){});
//now resolve the first promise, with empty string, to start the request chain
resolveStart('');
function createRequestPromise(lastId) {
return getPhotosDataFromRequest(userName, lastId).then(function (data)
{
lastId = data[data.length - 1].id;
console.log(data);
//stop the chain by rejection
if (!data.more_available) return Promise.reject('done');
return lastId;
});
}
Related
I am hoping this is an easy questions, and I just missed something simple. I have a chain of promise returning functions that flow using .then(). My final function takes the data I return, and formats into the correct format, which is then passed back to the client. The issue I am having (I think), is that the server sends the ff variable to the client before the promise from the formatting function is getting resolved. What confuses me, is that when I log the ff variable, it is logging with all the data as expected.
Any thoughts on why this is occurring, what I am doing wrong, and most importantly how to resolve this?
app.get("/subData", function(req, res) {
getConnection().then(function() {
return getSQL();
}).then(function(sql) {
return executeQuery(sql);
}).then(function(data) {
return formatData(data);
}).then(function(ff) {
console.log(ff);
res.status(200).send(ff);
}).catch(function(err) {
console.log("ERR:");
console.log(err);
res.status(405).send(err);
});
});
Here is the final format data function:
function formatData(data) {
var finArr = Array();
return new Promise(function(resolve, reject) {
data.rows.forEach(function(row, fin) {
var tempArr = Array();
row.forEach(function(itm, pos) {
var ttl = data.metaData[pos].name;
var val = itm;
tempArr[ttl] = val;
});
finArr.push(tempArr);
if(fin + 1 == data.rows.length) {
resolve(finArr);
}
});
});
}
It's pretty clear that one of your promises isn't resolving. Why make the resolve() call in your formatData() method conditional? Why not just let the loop run out?
function formatData(data) {
var finArr = Array();
return new Promise(function(resolve, reject) {
data.rows.forEach(function(row, fin) {
var tempArr = Array();
row.forEach(function(itm, pos) {
var ttl = data.metaData[pos].name;
var val = itm;
tempArr[ttl] = val;
});
finArr.push(tempArr);
});
resolve (finArr);
});
}
Indeed, you don't need a Promise here at all, because it's a synchronous operation. And, it's OK to use synchronous operations in .next() chains. Like this.
function formatData(data) {
var finArr = Array();
data.rows.forEach(function(row, fin) {
var tempArr = Array();
row.forEach(function(itm, pos) {
var ttl = data.metaData[pos].name;
var val = itm;
tempArr[ttl] = val;
});
finArr.push(tempArr);
});
return finArr;
}
The issue had nothing to do with promises resolving properly. The issue was in how I was formatting my data to be sent back to the client. I was building an array of associative arrays with the following code. When it was sent to the client, it was being sent as a blank array of arrays.
data.rows.forEach(function(row, fin) {
var tempArr = Array();
row.forEach(function(itm, pos) {
var ttl = data.metaData[pos].name;
var val = itm;
tempArr[ttl] = val;
});
finArr.push(tempArr);
});
return finArr;
What I needed to do was send the data as an array of objects and then convert it to an array of arrays on the client side. By adjusting my code to this, the data properly passed over as I needed.
data.rows.forEach(function(row, fin) {
// Change From var tempArr = Array() to var tempArr = {} to initialize new Object
var tempArr = {};
row.forEach(function(itm, pos) {
var ttl = data.metaData[pos].name;
var val = itm;
tempArr[ttl] = val;
});
finArr.push(tempArr);
});
return finArr;
I recently posted an issue I had with another Parse CloudCode method, were the error was thrown that Error: success/error was not called. I am having that issue again but with a different method/scenario.
Parse.Cloud.define("background", function(request, response) {
var moments = require("cloud/moments.js");
var now = moments.moment();
var query = new Parse.Query("Group");
query.find({
success: function(results) {
for (var i = 0; i < results.length; i++) {
var object = results[i];
var events = object.get("Events");
var getUsers = false;
for (var q = 0; q < events.length; q++) {
var e = events[q];
if (e.get("date") == now) {
getUsers = true;
break;
}
}
if (getUsers == true) {
for (var q = 0; q < events.length; q++) {
var e = events[q];
if (e.get("date") == now) {
var relation = object.relation("created");
var partOne = e.get("name");
var outString1 = partOne.concat(" is now");
// generate a query based on that relation
var query = relation.query();
Parse.Push.send({
where: query, // Set our Installation query
data: {
alert: outString1
}
}, {
success: function() {
// Push was successful
},
error: function(error) {
// Handle error
}
});
var relation2 = object.relation("joined");
var partOnee = e.get("name");
var outString = partOnee.concat(" is now");
// generate a query based on that relation
var query2 = relation.query();
Parse.Push.send({
where: query2, // Set our Installation query
data: {
alert: outString
}
}, {
success: function() {
// Push was successful
},
error: function(error) {
// Handle error
}
});
e.destroy();
}
}
}
}
}
});
response.success();
});
Since this method involves more than just a simple query and return (as it has the for loop among other things) I am a bit confused on how to implement the Parse Promise stuff. If anyone could assist me in how I should go about implementing the promise stuff it would be much appreciated.
Parse documentation is very clear on how to use Promises and how to rewrite your pyramid code with .then() blocks instead.
I have a Node app that I'm writing where I need to use promises for async calls.
I currently have a foreach loop being called from within a .then(function()) of a promise, but when I return the end result of the foreach, I get nothing.
In the foreach I can console.log the value of data and retrieve it, but not outside the for loop before the return?
var Feeds = function(){
this.reddit = new Reddit();
}
Feeds.prototype.parseRedditData = function(){
var _this = this;
this.getData(this.reddit.endpoint).then(function(data){
return _this.reddit.parseData(data, q);
});
}
Feeds.prototype.getData = function(endpoint){
var deferred = q.defer();
https.get(endpoint, function(res) {
var body = '';
res.on('data', function(chunk) {
body += chunk;
});
res.on('end', function() {
deferred.resolve(JSON.parse(body));
});
}).on('error', function(e) {
deferred.reject(e);
});
return deferred.promise;
}
var Reddit = function(){
this.endpoint = "https://www.reddit.com/r/programming/hot.json?limit=10";
}
Reddit.prototype.parseData = function(json, q){
var dataLength = json.data.children.length,
data = [];
for(var i = 0; i <= dataLength; i++){
var post = {};
post.url = json.data.children[i].data.url;
post.title = json.data.children[i].data.title;
post.score = json.data.children[i].data.score;
data.push(post);
}
return data;
}
Feeds.prototype.parseRedditData = function(){
var _this = this;
this.getData(this.reddit.endpoint).then(function(data){
return _this.reddit.parseData(data, q);
});
}
When i see this I see a "return" in the callback of the promise... I don't know why you're doing this, but I just want to be sure:
I you want this "return" to be the returned value of the function 'parseRedditData', this won't work.
The only way to return your data here is by using a callback, or a promise, like this:
Feeds.prototype.parseRedditData = function(callack){
var _this = this;
this.getData(this.reddit.endpoint).then(function(data){
callback(_this.reddit.parseData(data, q));
});
}
For my project I have a server.js that calls a helper function place-search.js as shown below.
var express = require('express');
var server = express.Router();
var placeSearch = require("./helpers/place-search");
var obj = "hello";
server.use(function(req, res, next) {
console.log(req.method, req.url);
next();
});
server.post('/', function(req, res) {
/* get the object passed by the client's post request */
obj = req.body;
//console.log("Obj: " + obj);
/* send the confirmation back to the client */
res.status(200).send("body");
placeSearch.placeSearch(obj);
});
module.exports.server = server;
Here is my place-search.js :
var config = require("./config.js");
var Promise = require('bluebird');
var DistanceMatrix = require("./distance-matrix.js");
var GooglePlaces = Promise.promisifyAll(require("googleplaces"));
var googlePlaces = new GooglePlaces(config.apiKey, config.outputFormat);
var extract = require('./extract.js');
var combination = require('./combination_ver2.js');
var permutation = require('./permutation.js');
function placeSearch(obj) {
console.log("Inside place search!");
/**
* Place search - https://developers.google.com/places/documentation/#PlaceSearchRequests
*/
var arr = [];
var count = 0;
var rad = obj["radius"];
console.log("radius: " + rad);
var loc = obj["location"];
console.log("Location: " + loc);
var mode = obj["mode"];
var params = obj["params"];
/* client's keywords */
var arr;
var ar = [];
for (var i = 0; i < params; i++) {
arr[i] = obj[i];
console.log(arr[i]);
var param = {
location: loc,
radius: rad,
mode: mode,
keyword: arr[i]
};
ar.push(param);
}
console.log("before promises");
var promises = ar.map(function(name) {
return googlePlaces.placeSearch(name, function(response) {
arr.push(response);
console.log(response);
console.log(count++);
//If all responses have been returned
//Find combos and pass to distance-matrix
if (count == ar.length) {
var Matrix = new Array();
var result = new Array();
//to extract only lat and lng from arr.results
//Matrix = extract.extract(arr);
result = combination.combination(arr);
// NOW RESULT IS THE ARRAY OF ALL COMBINATION
// NOW RESULT IS THE ARRAY OF COMBINATIONS OF latlng pairs AND PASS IT TO FRONTEND
/*result.forEach(function(combo, index) {
console.log("combo" + combo)
DistanceMatrix.distanceMatrix(mode, combo, result.length);
});*/
// IF YOU WANT TO SEE PERMUTATION
//permutation.permutation(result);
console.log("combination results: " + result);
}
})
});
}
module.exports.placeSearch = placeSearch;
My problem is I do not know how to pass the result variable back to the server.js so that I can use that result as an input for another helper function. I can not for the life of me figure out how to do this. Any help at all would be greatly appreciated.
Well, I don't see your placeSearch function returning anything at all right now, nor doing any kind of callback. Your placeSearch function should expose a callback parameter, which then gets called once you have the answer you want to send back.
Your server file will then take action on that callback. Abbreviating your code, it'd look something like this:
server.post('/', function(req, res) {
/* get the object passed by the client's post request */
obj = req.body;
//console.log("Obj: " + obj);
placeSearch.placeSearch(obj, function(error, data){
/* send the data back to the client */
res.status(200).send(data);
});
});
To support that, your placeSearch function will have to call its callback when appropriate:
function placeSearch(obj, callback){
/* all the stuff you do to assemble your data */
// if (there_is_some_error):
if(err) return cb(err);
// when you have all your data available, no error has occurred
return cb(null, data);
}
Something else you might notice is that your ar.map won't work as you seem to expect. ar.map is a synchronous function, you're calling async code inside... not gonna work the way you think. It's a bit long for this post, but you should look at the async library from npm to manage an array of asynchronous requests to collect one combined result.
use callback your code looks like this:
function placeSearch(obj,callback) {
console.log("Inside place search!");
/**
* Place search - https://developers.google.com/places/documentation/#PlaceSearchRequests
*/
var arr = [];
var count = 0;
var rad = obj["radius"];
console.log("radius: " + rad);
var loc = obj["location"];
console.log("Location: " + loc);
var mode = obj["mode"];
var params = obj["params"];
/* client's keywords */
var arr;
var ar = [];
for (var i = 0; i < params; i++) {
arr[i] = obj[i];
console.log(arr[i]);
var param = {
location: loc,
radius: rad,
mode: mode,
keyword: arr[i]
};
ar.push(param);
}
console.log("before promises");
var promises = ar.map(function(name) {
return googlePlaces.placeSearch(name, function(response) {
arr.push(response);
console.log(response);
console.log(count++);
//If all responses have been returned
//Find combos and pass to distance-matrix
if (count == ar.length) {
var Matrix = new Array();
var result = new Array();
//to extract only lat and lng from arr.results
//Matrix = extract.extract(arr);
result = combination.combination(arr);
// NOW RESULT IS THE ARRAY OF ALL COMBINATION
// NOW RESULT IS THE ARRAY OF COMBINATIONS OF latlng pairs AND PASS IT TO FRONTEND
/*result.forEach(function(combo, index) {
console.log("combo" + combo)
DistanceMatrix.distanceMatrix(mode, combo, result.length);
});*/
// IF YOU WANT TO SEE PERMUTATION
//permutation.permutation(result);
console.log("combination results: " + result);
callback(null,result);
}
})
});
}
in server.js:
server.post('/', function(req, res) {
/* get the object passed by the client's post request */
obj = req.body;
//console.log("Obj: " + obj);
/* send the confirmation back to the client */
res.status(200).send("body");
placeSearch.placeSearch(obj,function(err,result){
if(!err){
console.log(result);
}
})
});
It seems like you're having trouble with the async operation. You'll want to return the promise from your place-search module. You'll also need to convert the callbacks from placeSearch into a promise.
EDIT: updated since googlePlaces.placeSearch doesn't return a promise
inside placeSearch
function placeSearch(obj) {
//...
var promises = ar.map(function(name) {
var placeDefer = Q.defer();
return googlePlaces.placeSearch(name, function(response) {
placeDefer.resolve(response); // or placeDefer.reject if a failure occurs
});
return placeDefer.promise;
});
return promises;
}
and in your route:
// I'm going to just assume Q promise library here
var Q = require("q");
server.post('/', function(req, res) {
/* get the object passed by the client's post request */
obj = req.body;
//console.log("Obj: " + obj);
/* send the confirmation back to the client */
res.status(200).send("body");
Q.all(placeSearch.placeSearch(obj))
.spread(function() {
var places = Array.prototype.slice.call(arguments);
// possibly call res.status(200).send("body"); here?
// only if you're trying to use the places in your response
});
});
I am building a Windows 8 Store app with HTML/CSS/JavaScript. I am reading in data from a text file through a function, and then putting that data into an array. I am trying to return the array through the function, but it is not working. Any help would be greatly appreciated. I've attached my code snippet.
// Load user data
var DefineUserData = function LoadUserData() {
return Windows.Storage.ApplicationData.current.localFolder.getFileAsync(loadfile).done(function (UserFile) {
return Windows.Storage.FileIO.readTextAsync(UserFile).done(function (fileResult) {
var userdata = new Object();
var dataobject = {};
var innercount;
var outercount;
var fileResultByLines = fileResult.split("\n");
for (outercount = 0; outercount <= (fileResultByLines.length - 2) ; outercount++) {
var tempArray = fileResultByLines[outercount].split(",");
dataobject.metrictitle = tempArray[0];
dataobject.numinputs = tempArray[1];
dataobject.inputs = new Array();
for (innercount = 0; innercount <= parseInt(dataobject.numinputs) ; innercount++) {
dataobject.inputs[innercount] = tempArray[innercount + 2];
}
userdata[outercount] = dataobject;
}
return userdata;
});
},
function (errorResult) {
document.getElementById("resbutton1").innerText = errorResult;
})
}
Your DefineUserData function is returning a Promise, not a value. Additionally done functions don't return anything. Instead you'll need to use then functions instead of done functions in DefineUserData and then handle add a done function (or then) to the code that calls this function.
Also, You can make your promises easier to read, and easier to work with by chaining then functions instead of nesting them.
Currently on Win7 at the office so I can't test this, but try something similar to this pseudo-code. Note then functions instead of done. The last then returns your data. Sample snippet afterwards to illustrate calling this and handling the result.
// modified version of yours
var DefineUserData = function LoadUserData() {
return Windows.Storage.ApplicationData.current.localFolder
.getFileAsync(loadfile)
.then(function (UserFile) {
return Windows.Storage.FileIO.readTextAsync(UserFile);
}).then(function (fileResult) {
var userdata = new Object();
var dataobject = {};
var innercount;
var outercount;
var fileResultByLines = fileResult.split("\n");
for (outercount = 0; outercount <= (fileResultByLines.length - 2) ; outercount++) {
var tempArray = fileResultByLines[outercount].split(",");
dataobject.metrictitle = tempArray[0];
dataobject.numinputs = tempArray[1];
dataobject.inputs = new Array();
for (innercount = 0; innercount <= parseInt(dataobject.numinputs) ; innercount++) {
dataobject.inputs[innercount] = tempArray[innercount + 2];
}
userdata[outercount] = dataobject;
}
return userdata;
},
function (errorResult) {
document.getElementById("resbutton1").innerText = errorResult;
});
}
// some other code...
DefineUserData.done(function (userdata) {
// do something
});