Lose refernce to object within method [duplicate] - javascript

This question already has answers here:
How to access the correct `this` inside a callback
(13 answers)
Closed 7 years ago.
I created a simple example of my problem
The function count should count the numbers of items that I receive back from a query. However the way I am currently implementing it I lose reference to my function when I call from the route. In this case the function doWork is from another node module that I cannot modify.
Is there anyway to get around this
function Counter(){
this.array = createArray();
};
Counter.prototype.count = function (q){
query(q, function(data){
if(data === "tabe")
{
this.array[0].total++;
}
else
if(data === "chair")
{
this.array[1].total++;
}
else
if(data === "lamp")
{
this.array[2].total++;
}
});
};
createArray = function (){
var array = [];
array.push({item : "table",
total: 0});
array.push({item : "chair",
total: 0});
array.push({item : "lamp",
total: 0});
return array;
};
//The query function is actually in another node module that I cannot edit
query = function( data, callback){
callback(data);
}
module.exports = Counter;
index.js file
/* Process query */
router.get('/submit', function(req, res, next) {
var counter = new Counter();
counter.count("table");
counter.count("table");
counter.count("lamp");
for(var i = 0; i < counter.array.length; i++){
console.log(counter.array[i]);
}
res.end();
});

It is because the execution context of the callback method is not referring to the Counter instance, you can use the Function.bind() to pass a custom context to the callback method.
Counter.prototype.count = function (q) {
query(q, function (data) {
if (data === "tabe") {
this.array[0].total++;
} else if (data === "chair") {
this.array[1].total++;
} else if (data === "lamp") {
this.array[2].total++;
}
}.bind(this));
};
Another option is to use a closure variable
Counter.prototype.count = function (q) {
var self = this;
query(q, function (data) {
if (data === "tabe") {
self.array[0].total++;
} else if (data === "chair") {
self.array[1].total++;
} else if (data === "lamp") {
self.array[2].total++;
}
});
};

Related

Function returns "empty" response [duplicate]

This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 5 years ago.
I have a problem how to properly call/return one function data. I have this function displayTableWithCountryStates which is calling getCountryStates function. The problem is that when i make request $.get i get proper response, but when i try to return this response, console.log(countryStates) inside displayTableWithCountryStates is empty
countryStatesTables = {
displayTableWithCountryStates: function (source, newForm) {
var countryId = 237;
var countryStates = countryStatesTables.getCountryStates(countryId);
console.log(countryStates); // Response is empty
},
getCountryStates: function (countryId) {
if (countryId !== '' || countryId !== 'undefined') {
$.get(balthazar.settings.logistics.getCountryStatesUrl.url + '/' + countryId, function (data) {
console.log(data.data); //Response is ok, data is present
return data.data;
});
}
}
};
Why and how to properly return data in my displayTableWithCountryStates function. If you need any additional informations, please let me know and i will provide. Thank you!
Asynchronous functions need callbacks to handle the data as we don't know exactly when they would return the output. You can also it with promises.
countryStatesTables = {
displayTableWithCountryStates: function (source, newForm) {
var countryId = 237;
var countryStates = this.getCountryStates(countryId, function(data){
console.log(countryStates);
});
},
getCountryStates: function (countryId, callback) {
if (countryId !== '' || countryId !== 'undefined') {
$.get(balthazar.settings.logistics.getCountryStatesUrl.url + '/' + countryId, function (data) {
console.log(data.data); //Response is ok, data is present
callback(data.data);
});
}
}
};

Node JS losing for loop index variable [duplicate]

This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
Closed 6 years ago.
When I use a simple for loop to access array values, the index variable gets lost therefore unable to access array. Using index number instead of variable works but not variable. This is the most annoying code ever.
/* jshint esnext: true, asi: true */
var neo4j = require('node-neo4j')
// Create the neo4J object
var db = new neo4j(serverURI)
exports.addPerson = (body, callback) => {
if (body.skills) {
var sentSkills = body.skills
var arraySkills = sentSkills.split(',')
}else {
var sentSkills = []
}
const sentName = body.name
const sentEmail = body.email
const sentUsername = body.username
const sentPassword = body.password
const lecturerStatus = body.lecturer
db.readNodesWithLabelsAndProperties('Person',{ email: sentEmail }, function (err, node) {
if (err) {
return console.log(err)
}
if (node.length > 0){
// The user already exists
callback({code:401,status:'failed',message:'Person already exsits with the name '+sentName,data:sentName})
}
else {
// Insert new Person
db.insertNode({
name: sentName,
email: sentEmail,
skills: sentSkills,
username: sentUsername,
password: sentPassword,
lecturer: lecturerStatus
}, 'Person', function (err, node) {
personNode = node
if (err) {
return console.log(err+1)
}
else {
// I hate you for not working
// The i = 0 variable is not accessible -> arraySkill[i]^.trim()
// ERROR: cannot read property trim of undefined
console.log("success")
for (i = 0; i < arraySkills.length; i++){
arraySkills = body.skills.split(',')
db.cypherQuery("MATCH (s:Skill {name:'"+arraySkills[i].trim()+"'}) RETURN s", function(err, node){
if (err){
console.log("ERROR1")
console.log(err)
}
else {
console.log(arraySkills[0])
if (node.data == '')
{
db.cypherQuery("CREATE (s:Skill {name:'"+arraySkills[i].trim()+"'}) RETURN s", function(err, node){
if (err){
console.log("ERROR2")
console.log(err)
}
else {
console.log(node)
db.cypherQuery("MATCH (p:Person), (s:Skill) WHERE p.name = '"+sentName.trim()+"' AND s.name = '"+arraySkills[i].trim()+"' CREATE (p)-[r:knows]->(s) RETURN r", function(err, node){
if (err){
console.log("ERROR3")
console.log(err)
}
else {
console.log(node)
console.log("Success")
}
})
}
})
}
}
})
};
}
})
// Output node data.
callback({code:201,status:'success',message:'Person Added In '+sentName+' found...',data:node})
}
})
}
That's a closure problem, to fix it you have to move your $http call to a new function like this.
for (i = 0; i < arraySkills.length; i++){
var skills = body.skills.split(',');
dbQuery(skills,i); // In this function you have to write all the stuff you got under arraySkills = body.skills.split(',')
}

Node.JS callback inside while [duplicate]

This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 7 years ago.
What I want to do:
var found = false;
while (!found){
var result = db.getNextRecord();
if (result == search_term){
return result;
}
}
The problem is, getNextRecord is asynchronous
var nothing_returned = db.getNextRecord(function(err, result){
// I have the result in this callback, but not before
});
Given the behavior of getNextRecord(cb), how can I rewrite the above code snippet to get the same outcome?
Since you have a function that's async and you want to call in synchronously, you have two choice. Use a sync version of the method if there is one available, but if not, then you'll have to change your logic.
The following snippet should do what you want, it does require the async library.
var async = require('async');
var result;
async.whilst(
function () {
return !result;
},
function (callback) {
db.getNextRecord(function (err, record) {
if (err)
{
return callback(err);
}
if (result == search_term)
{
result = record;
}
return callback();
});
},
function (err) {
// Search is complete, do what you wish with result in this function. This function
// will be called when whilst is done or if getNextRecord got an error.
}
);
I'm sure there's a shorter way to do this if you want to change the logic even more, but this is similar to doing a while but asynchronously.
Use the async library. Its until function looks like what you need: https://www.npmjs.com/package/async#until
var async = require('async');
var latestResult = null;
async.until(function () {
return latestResult == search_term;
}, function () {
db.getNextRecord(function (err, result) {
latestResult = result;
});
}, function () {
// now you can do something with latestResult
});
You should also consider whether it makes sense to do this in your app or have the database query include this filtering.
With babel and new JS:
import {promisify as pr} from 'es6-promisify';
async function find(search_term) {
let found = false, result=null;
while (!found){
let result = await pr(db.getNextRecord)();
if (result == search_term){
found=true;
}
}
return result;
}

NodeJS cannot create array with nested query

After a succesful query to Mongodb to get a list of news, for the news that have a link attached i search for link details in the database but after i set them inside the modified news object i cannot push it to an array. Why does this happen?
var newsarray = []
for(i=0;i<newsfound.length;i++){
if(!newsfound[i].link._id){
newsarray.push(newsfound[i])
} else {
var tempnew = newsfound[i];
db.findOne('links',{'_id':tempnew.link._id},function(err,linkdetails){
if(err){
console.log(err)
} else {
tempnew.linkdetails = linkdetails;
newsarray.push(tempnew)
}
})
}
}
console.log(newsarray)
The result of this is an array without the link containing news or if i try a few changes an array with the original news without the added details.
You can not use an asynchronous function inside a for loop. The reason is that the for loop gets executed before even any callback could come, and hence the scope gets messed up.
You should use recursive function (self calling) which will prevent the next async call before the previous is over.
var i = 0;
function fn() {
// your logic goes here,
// make an async function call or whatever. I have shown async in a timeout.
setTimeout(function () {
i += 1;
if (i < newsfound.length) {
fn();
} else {
// done
// print result
}
});
}
Update:
For your use case,
var newsarray = []
var i = 0;
function done() {
// done, use the result
console.log(newsarray);
}
function fn() {
if (!newsfound[i].link._id) {
newsarray.push(newsfound[i]);
i += 1;
if (i < newsfound.length) {
fn();
} else {
done();
}
} else {
var tempnew = newsfound[i];
db.findOne('links', {'_id':tempnew.link._id}, function(err, linkdetails){
if(err){
console.log(err)
} else {
tempnew.linkdetails = linkdetails;
newsarray.push(tempnew);
i += 1;
if (i < newsfound.length) {
fn();
} else {
done();
}
}
})
}
}

How to simplify my javascript code(ajax)

I use ajax in my code. I have to call connectHost() from $('SyncDataPort0') to $('SyncDataPort5'),
function connectHost()
{
ajaxFrame($('SyncDataPort0').value, getConnectStatus);
}
function getConnectStatus(transport)
{
try {
rs = transport.responseText;
if(rs == 'OK') {
//$('SyncDataState0').innerHTML = 'ok';
addStateMsg($('ConnectTest'),getMsg('msgConnectOk'));
} else //NOT OK
addStateMsg($('ConnectTest'),getMsg('msgConnectNotOkResult').replace('%s',rs));
}catch(e){alert(e)};
}
function ajaxFrame(url, pars, onCompleteFun)
{
if (3 in arguments)
addStateMsg(arguments[3],getMsg('msgDataSending'),0);
new Ajax.Request(url,
{
method:'post',
parameters:pars,
onComplete: function(transport)
{
var rs = transport.responseText;
if('logout' == rs)
location.href='/index.php?menu=logout';
else if('' == rs)
{
//do nothing
}else
onCompleteFun.apply(this,[transport]);
},
onFailure:function()
{
debug('Load Data Failure!');
}
});
return true;
}
The question is how can i implement the function without reproducing the getConnectStatus
callback function???
If you use an inline function declaration, you can refer to variables in the parent scope and you can pass the port to your connectHost() function.
function connectHost(portNum)
{
ajaxFrame($('SyncDataPort' + portNum).value, function(transport) {
// you can refer to portNum here in the callback
try {
rs = transport.responseText;
if(rs == 'OK') {
//$('SyncDataState0').innerHTML = 'ok';
addStateMsg($('ConnectTest'),getMsg('msgConnectOk'));
} else //NOT OK
addStateMsg($('ConnectTest'),getMsg('msgConnectNotOkResult').replace('%s',rs));
} catch(e) {alert(e)};
});
}
If you want getConnectStatus() to still be its own function, then you can use an inline stub function like this:
function connectHost(portNum)
{
ajaxFrame($('SyncDataPort' + portNum).value, function(transport) {
getConnectStatus(transport, portNum);
});
}
And getConnectStatus() will have the portNum as the second argument. You can pass as many arguments through to the callback as you like this way.
If getConnectStatus() needs the value of this preserved, then you would do this:
function connectHost(portNum)
{
ajaxFrame($('SyncDataPort' + portNum).value, function(transport) {
getConnectStatus.call(this, transport, portNum);
});
}

Categories

Resources