I have an array of JSON objects. This array is within a for loop and it keeps adding them to the array as the information becomes available from the database. The number of the objects in the array might not be the same as the number of results returned from the database (because only some locations might have the type of food a user is searching for).
For instance, I can have 12 restaurants returned in rows
, however only 3 sell hamburgers, so I can't simply do if (rows.length - 1 == i)
, because i
is only going to reach 2 while rows.length - 1
is 11.
So the matching returned results (JSON) are added one by one in a for loop. I can never preemptively know how many restaurants sell burgers until all those restaurants that do are added to the array.
I've tried a variety of tricks and the usual error I get from node is "can't send headers more than once." And I know why it's giving me this error. It's giving me this error because every iteration of the loop it will return whatever it has in the array.
An example of output
First iteration:
{ "results": [ {"name_of_restaurant": "joes burgers", "open_now": true }] }
Second iteration:
{ "results": [ {"name_of_restaurant": "joes burgers", "open_now": true }, { "name_of_restaurant": "five guys", "open_now": true }] }
Third iteration:
{ "results": [ { "name_of_restaurant": "joes burgers", "open_now": true }, "{ name_of_restaurant": "five guys", "open_now": true }, " { name_of_restaurant": "shake shack", "open_now": true }] }
I want a way to capture the third iteration so I can send that back to the client.
To be clear, I am not looking for array.length - 1
. My problem is substantially more complex.
EDIT - code added
function retrieveLocation(callback) {
var locationsWithinVisibleMapRectQuery = "SELECT * FROM locations WHERE Y(coordinates) > " + req.body.SWCoordLat + "AND Y(coordinates) < " + req.body.NECoordLat + "AND X(coordinates) > " + req.body.SWCoordLong + "AND X(coordinates) < " + req.body.NECoordLong + ";";
connection.query(locationsWithinVisibleMapRectQuery, function(err, rows) {
if (err) throw err;
var jsonObject = {
"results" : []
};
//console.log("Number of businesses: " + rows.length);
for(var i = 0; i < rows.length; i++) {
console.log("Business number " + i);
var businessName = rows[i].name;
console.log(businessName);
console.log();
var x = rows[i].coordinates.x;
var y = rows[i].coordinates.y;
getMenuForEachLocation(x, y, businessName, rows, i, function(err, obj) {
if (err) {
callback(err, null);
}
jsonObject["results"].push(obj);
if( jsonObject["results"] == the last index) { // figure a way to get last iteration to send back as a response
callback(null, jsonObject);
}
});
}
});
}
retrieveLocation(function(err, jsonObject) {
if (err) throw err;
res.json(jsonObject);
});
An working example of an approach that checks for .length
of results
array to be equal to .length
of rows
array. Note, since results
is filled asynchronously, the resulting array may not be in order of i
var rows = [0, 1, 2, 3, 4, 5, 6] , results = [] , asyncFn = function(n) { return new Promise(function(resolve) { setTimeout(function() { resolve(n) }, Math.random() * 3000) }) } , complete = function(callback) { for (var i = 0; i < rows.length; i++) { asyncFn(i).then(function(data) { results.push(data); console.log(results); if (results.length === rows.length) callback(rows, results) }) } } complete( // `callback` : do stuff when `results` `.length` is equal to `rows` `.length` function(rows_, results_) { console.log(rows_, results_) alert("complete"); } );
As I understand, the callback function of getCheckinsForEachLocation()
only fire if the condition is met, so there is no way you can know when all the data is processed inside the callback function.
We currently know how many rows are with rows.length
, and what we need to know is when all the getCheckinsForEachLocation()
fired, so we need another index and a oncomplete
callback.
This is a working example:
var globalIndex; // Pseudo async function function getCheckinsForEachLocation (rows, i, callback, oncomplete) { setTimeout(function () { if (-1 != rows[i].indexOf('burgers')) { callback(null, rows[i]); } // Add up the times that the function was called to // find out if they have called all. if (++globalIndex == rows.length) { oncomplete(); } }, Math.random() * 3000); } function retrieveLocation(callback) { // Pseudo data retrived from database var rows = ["sandwich", "burgers 1", "salad", "burgers 2", "sushi", "burgers 3", "tea"]; var jsonObject = { "results" : [] }; // Reset the time that `getCheckinsForEachLocation` was called globalIndex = 0; for (var i = 0, rowsLength = rows.length; i < rowsLength; ++i) { console.log("Business number " + i); getCheckinsForEachLocation(rows, i, function(err, obj) { if (err) { callback(err, null); } jsonObject["results"].push(obj); }, function () { callback(null, jsonObject); }); } } retrieveLocation(function(err, jsonObject) { if (err) throw err; alert(JSON.stringify(jsonObject)); });
I mentioned promises earlier - this may work for your needs. I definitely recommend checking out more about promises. Quick note, this is all es6 - so don't get too tied up w/ the arrow function syntax etc. If you're running node 4.0 >= then this should work out of the box;
function retrieveLocation() {
const locationsWithinVisibleMapRectQuery = "SELECT * FROM locations WHERE Y(coordinates) > " + req.body.SWCoordLat + "AND Y(coordinates) < " + req.body.NECoordLat + "AND X(coordinates) > " + req.body.SWCoordLong + "AND X(coordinates) < " + req.body.NECoordLong + ";";
return new Promise((resolve, reject) => {
connection.query(locationsWithinVisibleMapRectQuery, (err, rows) => {
if (err) reject(err);
resolve(rows);
});
})
.then(rows => {
return Promise.all(rows.map(row => {
const businessName = row.name;
const x = row.coordinates.x;
const y = row.coordinates.y;
return new Promise((resolve, reject) => {
getCheckinsForEachLocation(x, y, businessName, rows, i, (err, result) => {
if (err) reject (err);
resolve(result);
})
})
.then(result => result)
.catch(err => {
throw new Error(err)
});
}));
})
.then(result => result[result.length - 1]);
}
retrieveLocation()
.then(jsonObject => res.json(jsonObject))
.catch(err => console.log(err));
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.