I'm brand new to express.js and API calls, and I can't figure out why this is crashing my server? Essentially it will run the first time through and render the page, but then crash the server saying:
TypeError: Cannot read property 'length' of undefined
for (var i = 0; i < data.businesses.length; i++) {
relevant section of code:
router.get('/:term/:radius/:lat/:lng', function (req, res) {
var yelp = require("yelp").createClient({
consumer_key: "xxxx",
consumer_secret: "xxxx",
token: "xxxx",
token_secret: "xxxx"
});
yelp.search({
term: req.params.term,
radius_filter: req.params.radius,
ll: req.params.lat + ',' + req.params.lng
},
function (error, data) {
var businessesArr = [];
if (typeof data) {
for (var i = 0; i < data.businesses.length; i++) {
businessesArr.push({
name: data.businesses[i].name,
image_url: data.businesses[i].image_url
});
}
res.render('selection', {
businesses: businessesArr
});
// console.log(data);
} else {
console.log(error);
}
});
});
This line:
if (typeof data) {
is always going to evaluate to true since it actually returns a string no matter what.
Replace it with something like:
if (data && data.businesses) {
Related
The following method should query in the conversationCollection for an entry with a given ObjectID.
const mongodb = require('mongodb')
const ObjectID = mongodb.ObjectID
app.get('/getConversations', (req, res) => {
verifyUser(req, res, function(result) {
if(result !== "false") {
for(var i=0; i<result.conversations.length; i++) {
var objectid = new ObjectID(result.conversations[i].toString())
conversationCollection.findOne({_id: objectid}, function(res2) {
console.log(res2.members)
res.end(res2)
})
}
} else {
res.end("Error")
}
})
})
The result object has e.g. following data:
{
// ...
conversations: [ 5ccdc51d22399918b45b33d4,
5ccdc52322399918b45b33d6 ],
// ...
}
The problem is that console.log(res2.members) always throws TypeError: Cannot read property 'members' of null. The query for the findOne-method seams to be wrong. I've tried some variants already:
conversationCollection.findOne({"_id": objectid}, function(res2)
conversationCollection.findOne({_id: new ObjectID(result.conversations[i].toString())}, function(res2)
conversationCollection.findOne({"_id": ew ObjectID(result.conversations[i])}, function(res2)
conversationCollection.findOne({"_id": result.conversations[i]}, function(res2)
Nothing works and every variant produces the same nullpointer-exception.
This is because the res2 holds error data which is null.
findOne function has two params in callback: 1st is error and other is data. either of them is null.
try this:
app.get('/getConversations', (req, res) => {
verifyUser(req, res, function(result) {
if(result !== "false") {
for(var i=0; i<result.conversations.length; i++) {
var objectid = new ObjectID(result.conversations[i].toString())
conversationCollection.findOne({_id: objectid}, function(err,res2) {
console.log(err)
console.log(res2.members)
res.end(res2)
})
}
} else {
res.end("Error")
}
})
})
I am using for-loop to iterate thru sheets in a google spreadsheet and access specific cell within each sheet. I have a google spreadsheet with five sheets Sheet1, Sheet2,.. , Sheet5. The value if the cell that I am reading is Value1,..., Value5.
I hope to get results displayed as follow:
Sheet1
Value1
Sheet2
Value2
:
Sheet5
Value5
However I get:
Sheet1
Undefined
Sheet2
Undefined
:
Sheet5
Undefined
Value1
Value2
:
Value5.
I realize that I need to use async/await, but can't figure the right away with google.spreadsheet.get.
Thanks in advance for any guidance.
My code:
function getData (auth) {
var sheets = google.sheets('v4')
sheets.spreadsheets.get(
{
auth: auth,
spreadsheetId: sheet_id
},
(err, response) => {
if (err) console.log('The API returned an error: ' + err)
}
)
var no_sheets = response.data.sheets.length
for (var i = 0; i < no_sheets; i++) {
try {
console.log(response.data.sheets[i].properties.title)
var sheet_title = response.data.sheets[i].properties.title
var cell_value = getCellValue(auth, sheet_title)
console.log(cell_value)
} catch (err) {
console.log(err)
continue
}
}
}
function getCellValue (auth, sheet_title) {
sheets.spreadsheets.values.get(
{
auth: auth,
spreadsheetId: sheet_id,
range: sheet_title + '!A1:A1'
},
(err, response) => {
if (err) {
console.log('The API returned an error: ' + err)
return
}
var rows = response.data.values
if (rows.length === 0) {
console.log('No data found.')
} else {
for (var i = 0; i < rows.length; i++) {
var cell_value = rows[i]
}
console.log(cell_value)
return cell_value
}
}
)
}
If you want to use async/await instead of callbacks you can, because the googleapi returns a Promise. Those Promises are the foundation of async/await and were the goto before. If you use async/await or Promises your code will be much easier to read and understand.
So instead of calling:
sheets.spreadsheets.get({
auth: auth,
spreadsheetId: sheet_id
},
(err, response) => {
if (err) console.log('The API returned an error: ' + err)
});
you can call:
try {
const response = sheets.spreadsheets.get({
auth: auth,
spreadsheetId: sheet_id
});
}
catch(err) {
console.log('The API returned an error: ' + err);
}
Thing is that you can use the await keyword only in async functions. You can read more about async/await here.
given the following node\JavaScript code, for some reason the counters variables (failedCounter, successMatchedCounter, successUnmatchedCounter) are counted in an unexpectedly manner when running. I think that it's a matter of scopes and asynchronously but still can't spot the cause.
UPDATE: I think that I should be notified when all calls to connection.query() have finished and only then to log the counters..
See the line of "//BUG: counted numbers are not logged as expected":
var MongoClient = require('mongodb').MongoClient;
var mysql = require('mysql2');
var fs = require('fs');
var dir = './logs';
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir);
}
var testMode = false;
var mongoUrl = 'mongodb://xxx:27017/yy';
var mySqlconnString = {
host: 'xxxxx',
user: 'xxxx',
password: 'xxxxx',
database: 'xxxxx'
};
var connection = mysql.createConnection(mySqlconnString);
connection.connect(function(err) {
if (err) {
console.log('Error connecting to MySql DB');
return;
}
console.log('Connection established to MySql DB');
MongoClient.connect(mongoUrl, function(err, db) {
if (err) {
console.log('Error connecting to MongoDB');
return;
}
console.log("Connection established to MongoDB");
markSitesAsDeleted(db, function() {
console.log('closing DBs connections..');
connection.end(function(err) {});
db.close();
});
});
});
var failedCounter = 0;
var successMatchedCounter = 0;
var successUnmatchedCounter = 0;
var totalCounter = 0;
var markSitesAsDeleted = function(db, closeConnectionsCallback) {
console.log(`\nMigration process is starting..`);
var cursor = db.collection('someCollection').find({
"isDeleted": true
});
console.log(`Migrating data..\r\n`);
cursor.each(function(err, siteDoc) {
if (siteDoc != null) {
var siteID = Math.trunc(siteDoc._id)
if (testMode === false) {
connection.query(`CALL MarkSiteAsDeleted_3(${siteID})`, function(error, rows) {
if (error) {
//TODO: Print error
failedCounter++;
fs.appendFileSync('logs/log.txt', `Error occured when calling MarkSiteAsDeleted_3 SP for SiteId=${siteID}. see error: ${JSON.stringify(error)}\n`);
} else {
if (rows.affectedRows === 1) { // Has match
successMatchedCounter++;
} else {
successUnmatchedCounter++;
}
}
});
}
totalCounter++;
} else {
//BUG: counted numbers are not logged as expected
fs.appendFileSync('logs/log.txt', `Total: ${totalCounter}, Success and Matched: ${successMatchedCounter}, Success but Unmatched: ${successUnmatchedCounter}, Failed: ${failedCounter}\r\n`);
closeConnectionsCallback();
}
});
};
It sure looks like an async problem. Inside your cursor.each you have async callbacks to handle connection.query. So as the each iterates it makes queries. but all the queries are running asynchronously.
Once the each is finished setting up all the async queries it then hits your else which uses a synchronous write. That is where the problem occurs. Some of your async query handlers have not finished when that write occurs.
I've made a webscraper with cheerio and request and I'm trying now to implement a loop on an array of url.
Unfortunately I'm doing something wrong with my calls and callback but I can not figure out what.
This is my code :
var getWebData = function(url) {
var i = 1;
var data = [];
for (c = 0; c < url.length; c++) {
data[i] = request(url[c], function(err, resp, body) {
console.log('ok');
if (!err) {
console.log('there');
var $ = cheerio.load(body);
$('.text').each(function(i, element) {
var jsObject = { name : "", description : "", price: "", categorie: "", pricePerKg: "", capacity: "", weight: "", scrapingDate : "", url: ""};
var name = 'TESTOK';
jsObject.name = name;
data.push(jsObject);
})
return data;
}
console.log('but');
});
i++;
}
var json = JSON.stringify(data);
fs.writeFile('output.json', JSON.stringify(json, null, 4), function(err) {
console.log('File successfully written!');
})
}
getWebData(url);
app.listen('8080');
Note than any of my debugs print are not printed.
Does anyone know what's wrong in my code and how can I do to make it work ?
request is Aysnc
var json = JSON.stringify(data);
fs.writeFile('output.json', JSON.stringify(json, null, 4), function(err) {
console.log('File successfully written!');
})
This above code runs before the for loop completetes execution and populates data object.
Try executing this piece of code when loop complete execution.
run this command first npm install async --save
var async = require('async');
var getWebData = function(url){
var data = [];
async.eachSeries(url, function(urlSingle , cb){
request(urlSingle, function(err, resp, body) {
//write your logic here and push data in to data object
cb();
})
},function(){
// this will rum when loop is done
var json = JSON.stringify(data);
fs.writeFile('output.json', JSON.stringify(json, null, 4), function(err) {
console.log('File successfully written!');
});
});
}
I have been reading Asif's answer and the comments. That implementation is correct but you dont have to increment the c variable, also, if you initiate c=0 before, all the requests will be to url[0].
note that async.eachSeries callbacks each element of the array url in "urlsingle" callback, so you should use
request(urlsingle, ...
or consider using async.eachOf which gives you the index of each element in the array.
check for async documentation for any doubts http://caolan.github.io/async/
for (c = 0; c < url.length; c++) {
……
}
you should change like this:
var async = require('asycn');
async.map(url,
function(item, callback) {
data[i] = request(url[c],
function(err, resp, body) {
console.log('ok');
if (!err) {
console.log('there');
var $ = cheerio.load(body);
$('.text').each(function(i, element) {
var jsObject = {
name: "",
description: "",
price: "",
categorie: "",
pricePerKg: "",
capacity: "",
weight: "",
scrapingDate: "",
url: ""
};
var name = 'TESTOK';
jsObject.name = name;
data.push(jsObject);
}) callback(err, data);
}
console.log('but');
});
i++;
},function(err, results) {
if(err){
console.log(err);
}
});
in the loop is a time consuming operation.you should use asynchronous operation.
my openstates.billDetail function has a call back function inside of it, and I'm getting an error: 'callback must be a function', but my callback appears to be a function!
app.get('/search/:searchTerm', function(req, response) {
var nameArray = req.params.searchTerm.split('_');
var bills = []
var billIds = []
openstates.legSearch({
first_name: nameArray[0],
last_name: nameArray[1]
}, function(err, data) {
if (!err) {
openstates.billSearch({
state: 'CA',
chamber: 'lower',
page: '1'
}, function(err, data) {
for (var billIndex = 0; billIndex < data.length; billIndex++) {
billIds.push(data[billIndex].id)
}
for (var billIdIndex = 0; billIdIndex < billIds.length; billIdIndex++) {
openstates.billDetail(billIds[billIdIndex], function(err, data) {
console.log(data);
})
}
})
}
})
})
Anyone have any thoughts on this? my other callbacks work fine...
I tested your code, and it works perfectly fine.
I added the following to test it:
var openstates = {legSearch: function(a, cb){ cb(null, 'aa'); },
billSearch: function(a, cb){ cb(null, 'bb'); },
billDetail: function(a, cb){ cb(null, 'cc'); }};
Got no error, and the last function print 'cc' as expected.
BTW, you do not send any response back, I am not sure if this is on purpose.