Unhandled Promise rejection of a property undefined - javascript

setInterval(function(){
for(i=1;i<=len;i++)
{
plug[i].getSysInfo().then(data => {
var json = JSON.stringify(data,undefined,2);
var obj = JSON.parse(json);
var _macadd = obj.mac;
plug[i].getConsumption().then(data => {
var json = JSON.stringify(data,undefined,2);
var obj = JSON.parse(json);
var _current = obj.current;
var _voltage = obj.voltage;
var _power = (obj.power).toPrecision(5);
var _total = obj.total;
var _err_code = obj.err_code;
console.log(_power);
//console.log(_macadd);
//console.log(voltage);
if(_power > 1800)
{
_power = 0.00;
}
// var fs = require('fs');
// fs.writeFile("emeter.json", json);
var sql1 = "INSERT INTO emeter SET ? ";
var post1 = { macaddress: _macadd,current: _current,voltage: _voltage,power: _power, total: _total,err_code: _err_code};
var tmp = JSON.stringify(post1,undefined,2);
console.log(tmp);
con.query(sql1, post1, function (err, result) {
if (err) throw err;
console.log("inserted");
});
});
});
}
},30000);
While I run this, I am getting below error
Unhandled promise rejection Type error:
Cannot read property of 'getconsumption' of undefined.
The above code worked well when I replace the index with one value.To retrieve just one value it is working. When it is in For loop then the error message is returned. Please help me in solving this.

You're trying to use a for loop index value inside an asynchronous callback. The problem is that the .then() handlers are called AFTER the for loop has already finished and thus the for loop index is at its terminal value (beyond the end of the array) when you try to use it.
There are many ways to fix this. One simple way would be to change this:
for(i=1;i<=len;i++)
to this:
for(let i=1;i<=len;i++)
Using ES6 let creates a new unique variable for each execution of the for loop so that each one maintains its own copy of the index, even for the duration of the asynchronous calls within that for loop block.
In ES5 (where you don't have let), you could change your loop to use .forEach():
plug.forEach(function(val, index) {
// use val and index here rather than plug[i]
});
This also creates a new val and index variable for each invocation of the loop.

Related

Push inside forEach with query not working properly

I'm working with mongodb stitch/realm and I'm trying to modify objects inside an array with a foreach and also pushing ids into a new array.
For each object that i'm modifying, I'm also doing a query first, after the document is found I start modifying the object and then pushing the id into another array so I can use both arrays later.
The code is something like this:
exports = function(orgLoc_id, data){
var HttpStatus = require('http-status-codes');
// Access DB
const db_name = context.values.get("database").name;
const db = context.services.get("mongodb-atlas").db(db_name);
const orgLocPickupPointCollection = db.collection("organizations.pickup_points");
const orgLocStreamsCollection = db.collection("organizations.streams");
const streamsCollection = db.collection("streams");
let stream_ids = [];
data.forEach(function(stream) {
return streamsCollection.findOne({_id: stream.stream_id}, {type: 1, sizes: 1}).then(res => { //if I comment this query it will push without any problem
if(res) {
let newId = new BSON.ObjectId();
stream._id = newId;
stream.location_id = orgLoc_id;
stream.stream_type = res.type;
stream.unit_price = res.sizes[0].unit_price_dropoff;
stream._created = new Date();
stream._modified = new Date();
stream._active = true;
stream_ids.push(newId);
}
})
})
console.log('stream ids: ' + stream_ids);
//TODO
};
But when I try to log 'stream_ids' it's empty and nothing is shown. Properties stream_type and unit_price are not assigned.
I've tried promises but I haven't had success
It's an asynchronous issue. You're populating the value of the array inside a callback. But because of the nature of the event loop, it's impossible that any of the callbacks will have been called by the time the console.log is executed.
You mentioned a solution involving promises, and that's probably the right tack. For example something like the following:
exports = function(orgLoc_id, data) {
// ...
let stream_ids = [];
const promises = data.map(function(stream) {
return streamsCollection.findOne({ _id: stream.stream_id }, { type: 1, sizes: 1 })
.then(res => { //if I comment this query it will push without any problem
if (res) {
let newId = new BSON.ObjectId();
// ...
stream_ids.push(newId);
}
})
})
Promise.all(promises).then(function() {
console.log('stream ids: ' + stream_ids);
//TODO
// any code that needs access to stream_ids should be in here...
});
};
Note the change of forEach to map...that way you're getting an array of all the Promises (I'm assuming your findOne is returning a promise because of the .then).
Then you use a Promise.all to wait for all the promises to resolve, and then you should have your array.
Side note: A more elegant solution would involve returning newId inside your .then. In that case Promise.all will actually resolve with an array of the results of all the promises, which would be the values of newId.

Return array of array of objects with deferred

I am trying to get an array of "records" that each contain an array of "field objects" (each with properties .name, .value, and .fid).
Each array of records is obtained via an AJAX call to a database via an API call.
I want to be able to iterate through these later with some other code, but, for the moment, I'm trying to get the resultant array of arrays.
I'll be honest, I'm struggling with how to do this with promises...
If I was doing this synchronously I'd use a FOR TO loop and push each iteration into an array, but async is causing me issues...
My current code is as follows. This returns a single array of fields, rather tham an array of arrays. I think I need to push each 'thisArray' to another array. Anyone willing to help?:
NOTE:
ridList is a simple array of DB record IDs, e.g.: ["391", "392", "393", "394", "395", "398", "399", "400", "396", "397", "437"]
AVMI_db is the URL the AJAX call gets its data from.
///////////////////////////////////////////////////////////////
// Get record info for each array member
///////////////////////////////////////////////////////////////
function AVMI_getMultipleRecordInfoFromArray(ridList, AVMI_db) {
var promises = [];
$.each(ridList, function (index,value) {
var def = new $.Deferred();
var thisArray = [];
$.get(AVMI_db, { //******* ITERATIVE AJAX CALL *******
act: 'API_GetRecordInfo',
rid: value
}).then(function(xml2) {
$(xml2).find('field').each(function() {
var $field = {};
$field.fid = $(this).find('fid').text();
$field.name = $(this).find('name').text();
$field.value = $(this).find('value').text();
thisArray.push($field);
});
def.resolve(thisArray);
});
promises.push(def);
});
return $.when.apply(undefined, promises).promise();
};
You can create an array which is populated from as you iterate over the calls and return the array out after the promises are executed.
function AVMI_getMultipleRecordInfoFromArray(ridList, AVMI_db) {
var promises = [];
var returnValue = []; // to be returned by the promise
$.each(ridList, function (index,value) {
var def = new $.Deferred();
var thisArray = [];
$.get(AVMI_db, { //******* ITERATIVE AJAX CALL *******
act: 'API_GetRecordInfo',
rid: value
}).then(function(xml2) {
$(xml2).find('field').each(function() {
var $field = {};
$field.fid = $(this).find('fid').text();
$field.name = $(this).find('name').text();
$field.value = $(this).find('value').text();
thisArray.push($field);
});
returnValue.push(thisArray);
def.resolve(thisArray);
});
promises.push(def);
});
return $.when.apply(undefined, promises).then(function() {
return returnValue;
}).promise();
};
Maybe I'm missing something here, please feel free to comment if I didn't understood what you want. I undertood that you want to get an array of arrays instead of an array of fields. For this purpose, you gave the answer in your own question:
I think I need to push each 'thisArray' to another array.
But what puzzles me and makes me think I could have misunderstood something, is that if you can do code a bit complex as this, I think you could have made a simple change as that (or do you got this code somewhere ?), but anyway...
I can't test this because I would have to create the whole API call, but it should be as simple as:
///////////////////////////////////////////////////////////////
// Get record info for each array member
///////////////////////////////////////////////////////////////
function AVMI_getMultipleRecordInfoFromArray(ridList, AVMI_db) {
var promises = [];
$.each(ridList, function (index,value) {
var def = new $.Deferred();
var thisArray = [];
$.get(AVMI_db, { //******* ITERATIVE AJAX CALL *******
act: 'API_GetRecordInfo',
rid: value
}).then(function(xml2) {
$(xml2).find('field').each(function() {
var $field = [];
$field.push($(this).find('fid').text());
$field.push($(this).find('name').text());
$field.push($(this).find('value').text());
thisArray.push($fields);
});
def.resolve(thisArray);
});
promises.push(def);
});
return $.when.apply(undefined, promises).promise();

function return value is undefined

I'm currently working on this function, and the return value returns with undefined. I'm not sure if it is my lack of understanding how javascript operates, or it requires some tricky voodoo to make my code work.
var fs = require('fs');
var _ = require('underscore');
function eventValue(current, choice) {
var output = [];
if (current != null) {
var json;
fs.readFile('./json.json', 'utf8', function(err, data) {
if (err) {
throw err
}
json = JSON.parse(data);
// filter by ID
var filtered = _.filter(json, {
'ID': current
});
//console.log(filtered);
// Get ID of Object
var ID = _.pluck(filtered, 'ID');
// Get Next value of object
var NEXT = _.pluck(filtered, 'next');
// get nested 'choice's 'next' value
var collect = _.pluck(_.filter(
_.flatten(
_.pluck(filtered, 'choice')), {
'choice': choice
}), 'next');
var stringID = String(ID);
var stringNext = String(NEXT + collect);
output = [stringID, stringNext];
return output;
})
} else console.log("[[error]] please populate eventValue()");
};
var a = eventValue("001001A01B01");
console.log(a);
You are trying to use value returned from asynchronous function callback which you can not.
Refer: How to get returned value by function with callback inside

Property cannot be read after wrapping function

var sync = Meteor.wrapAsync(connection.query);
var rows = sync(selectMailgunChecked);
var emails = rows.map(function(a) {
return a.email
});
var from = 'sample#email.com';
emails.forEach(function(entry) {
mailgunSend( entry, from, contentHTML, contentRAW, subject, tags);
});
Code above (wrapped function of connection.query from node-mysql use in Meteor app) gives me an arror:
Cannot read property 'typeCast' of undefined
It is somehow related to sync(selectMailgunChecked) and external library (from node-mysql) Connection.js:
Connection.prototype.query = function query(sql, values, cb) {
var query = Connection.createQuery(sql, values, cb);
query._connection = this;
if (!(typeof sql == 'object' && 'typeCast' in sql)) {
query.typeCast = this.config.typeCast;
}
if (query.sql) {
query.sql = this.format(query.sql, query.values);
}
this._implyConnect();
return this._protocol._enqueue(query);
};
Every variable in my code is defined and sucessfully passed. What Can be wrong here?
this.config in this line
query.typeCast = this.config.typeCast;
is undefined.
You have to define the context (this) in which the wrap async function connection.query is executed by passing it as the second parameter
var sync = Meteor.wrapAsync(connection.query, connection);

Asynchronous for loop: how do I make the for loop wait for function to finish?

I'm racking my brain trying to figure out how to sequence this / place callbacks to get what I need.
I have a loop that checks for the existence of files. I'd like it to callback when it's done, but the for loop and the callback finish before the "fs.open" finishes... typical asynchronous problem.
I am using node v0.10.29, express v3.14.0, and am looking at using the "async" library, but again, just can't figure out the logic that I need...
Here is what I have:
Input
function checkForAllFiles(callback)
{
var requiredFiles = [];
requiredFiles[requiredFiles.length] = "../Client/database/one.js";
requiredFiles[requiredFiles.length] = "../Client/database/two.dat";
requiredFiles[requiredFiles.length] = "../Client/database/three.xml";
requiredFiles[requiredFiles.length] = "../Client/database/four.dat";
requiredFiles[requiredFiles.length] = "../Client/database/five.dat";
requiredFiles[requiredFiles.length] = "../Client/database/six.xml";
var missingFiles = [];
for(var r=0; r<requiredFiles.length; r++)
{
fs.open(requiredFiles[r], 'r', function(err, fd){
if(err)
{
missingFiles[missingFiles.length] = err.path;
console.log("found missing file = ", err.path);
}
});
console.log("r = ", r);
}
console.log("sending callback: ", missingFiles);
callback(missingFiles);
}
Output
0
1
2
3
4
5
sending callback: []
found missing file: ../Client/database/three.xml
Desired Output
0
1
found missing file: ../Client/database/three.xml
2
3
4
5
sending callback: ["../Client/database/three.xml"]
I would use the reject method in the async module (which I see you've already found). What it will do is return an array in its callback that contains any elements that don't match a specified check function. For the check function, I'd recommend just using fs.exists instead of watching for an error on fs.open.
Using those functions you can actually reduce the whole check to one line. Something like this:
function checkForAllFiles(callback)
{
var requiredFiles = [];
requiredFiles[requiredFiles.length] = "../Client/database/one.js";
requiredFiles[requiredFiles.length] = "../Client/database/two.dat";
requiredFiles[requiredFiles.length] = "../Client/database/three.xml";
requiredFiles[requiredFiles.length] = "../Client/database/four.dat";
requiredFiles[requiredFiles.length] = "../Client/database/five.dat";
requiredFiles[requiredFiles.length] = "../Client/database/six.xml";
async.reject(requiredFiles, fs.exists, callback);
}
callback will get called with an array that contains just the files that don't exist.
Use the async library and the eachSeries method. Example:
async.eachSeries(array,
function(element, next) {
// do something with element
next();
}
);
It will sequentially go through the array and process each element. Calling next goes to the next element. Series makes sure it does it in the order of the array, otherwise the order of going through the array is not guaranteed. If you have other async functions called within it, just pass the next function around and call it when done with all the needed functions and the next array element will be processed.
Maybe something like this:
var missingFiles = []
async.eachSeries(requiredFiles, function(file, nextFile){
fs.open(file, 'r', function(err, fd){
if(err)
{
missingFiles[missingFiles.length] = err.path;
console.log("found missing file = ", err.path);
}
nextFile();
});
console.log("r = ", file);
});
console.log("sending callback: ", missingFiles);
callback(missingFiles);

Categories