Calling a function as a variable - javascript

I've been going at a small snippet of code for like an hour or two now, but can't seem to figure out why my Javascript breaks down at the end of this code.
// Getting username from address bar and user ID from username
var siteHref = window.location.href;
var specifiedUser = siteHref.split('#');
var userName = specifiedUser[1];
var userURL = 'http://soundcloud.com/' + userName;
var idGetter = SC.get('/resolve', { url: userURL }, function(user) {
SC.get('/users/', function() {
var userInfo = new Array(3);
userInfo[0] = user.username;
userInfo[1] = user.id;
userInfo[2] = user.public_favorites_count;
console.log(userInfo);
console.log(userInfo[2]);
});
});
I've added the variables at the top for a bit of context. To explain this function passes the two parts of the array that I've specified into the console, which is perfect. Only when I call the variable is shows up as undefined?
I also tried wrapping this in an additional function and has no luck calling it.
Anyone have idea where I've gone wrong?

You are calling two asynchronous functions. The results you get in the second one are ONLY valid inside that callback function or in a function you call from there. You can't use them globally afterwards because the async functions have not yet finished yet and thus they are still undefined.
Plus, if you're asking about the userInfo variable, that goes out of scope as soon as the callback returns so it is not available anywhere else.
See the comments I added to your code:
// Getting username from address bar and user ID from username
var siteHref = window.location.href;
var specifiedUser = siteHref.split('#');
var userName = specifiedUser[1];
var userURL = 'http://soundcloud.com/' + userName;
var idGetter = SC.get('/resolve', { url: userURL }, function(user) {
SC.get('/users/', function() {
var userInfo = new Array(3);
userInfo[0] = user.username;
userInfo[1] = user.id;
userInfo[2] = user.public_favorites_count;
console.log(userInfo);
console.log(userInfo[2]);
// ** you must use userInfo here or call some other function
// ** and pass userInfo to it
});
});
// ** you cannot use userInfo here as it is both out of scope and
// ** has not yet been set
The idGetter value will contain whatever the first call to SC.get() returns which will NOT be the eventual asynchronous result. If you tell us what SC.get() is, then we might be able to help you understand what it returns.
If SC.get() is the SoundCloud function, then it appears that it returns nothing and thus that is why idGetter is undefined.
FYI, I confirmed here in the SoundCloud source that SC.get() does not return anything.
If all you're trying to do is to create a new function that contains all this code and will call a callback when the results are available, you can just define that function and then call it:
function getUserInfo(callback) {
// Getting username from address bar and user ID from username
var siteHref = window.location.href;
var specifiedUser = siteHref.split('#');
var userName = specifiedUser[1];
var userURL = 'http://soundcloud.com/' + userName;
SC.get('/resolve', { url: userURL }, function(user) {
SC.get('/users/', function() {
var userInfo = new Array(3);
userInfo[0] = user.username;
userInfo[1] = user.id;
userInfo[2] = user.public_favorites_count;
console.log(userInfo);
console.log(userInfo[2]);
// ** you must use userInfo here or call some other function
// ** and pass userInfo to it
callback(userInfo);
});
});
// ** you cannot use userInfo here as it is both out of scope and
// ** has not yet been set
}
// then, you can call it like this:
getUserInfo(function(userData) {
// you can use userData here
});

idGetter isn't a function, you can't call it. If you want it to be a function, write:
var idGetter = function () {
SC.get('/resolve', { url: userURL }, function(user) {
SC.get('/users/', function() {
var userInfo = new Array(3);
userInfo[0] = user.username;
userInfo[1] = user.id;
userInfo[2] = user.public_favorites_count;
console.log(userInfo);
console.log(userInfo[2]);
});
});
};
Then you can do idGetter() to run the function.

I can only assume that userInfo[2] is showing as undefined since it would not make since for userInfo to be undefined. If that is the case, then it is probable that user.public_favorites_count is undefined. Trying printing that out to the console, and when it shows up as undefined, you'll need to determine why that is happening. Without more information about what that object is, or the code returning that object, there's not much more help the community will be able to provide.

This seems to either be working, or is a step forward.
var idGetter = function () { SC.get('/resolve', { url: userURL }, function(user) {
SC.get('/users/', function() {
var userInfo = new Array(3);
userInfo[0] = user.username;
userInfo[1] = user.id;
userInfo[2] = user.public_favorites_count;
console.log(userInfo);
console.log(userInfo[2]);
});
});
};
var idGetter = idGetter();
Setting the variable before the function, and re-setting it after. Not sure if this is standard practise, but seems to work to some extent.

Related

NightwatchJS not passing variable

I've separated the variables for scoping purposes, I'm able to fill out all the variables with the respective DOM elements from url1 but when I try to pass them to url2 fields, they get set to undefined as if the variable was never set. Yet the console.log works... I've passed also a function to .setValue('#address-serviceability-form', '', '', function(e){ // set the value here}) but still I get undefined... Is this variable scoping issue?
describe('Ecosia.org Demo', function() {
var address ='';
var streetNumber = '';
var city = '';
var stateAndZip = '';
var state = ''
var zip = '';
before(browser => browser.url('url1.com'));
test('Extract random address', function (browser) {
browser
.getText('.rand_large li:first-child', function(result) {
address = result.value.split("\n");
streetNumber = address[0];
console.log('streetNumber', streetNumber) //this works and logs correctly
city = address[1].split(",")[0];
stateAndZip = address[1].split(",")[1];
state = stateAndZip.split(" ")[1];
zip = stateAndZip.split(" ")[2];
})
.url('url2.com')
.waitForElementVisible('form')
.setValue('#address-serviceability-form', streetNumber) // this is set to undefined
.pause(5000)
});
after(browser => browser.end());
});
This looks to me to be a problem with your understanding of NodeJS callbacks.
You should be able to fix this using async/await. Your code is not waiting for the callback to be resolved and the value is not assigned before being used in set value function. you could try something like below.
test('Extract random address', async function(browser) {
await browser
.getText('.rand_large li:first-child', function(result) {
address = result.value.split("\n");
streetNumber = address[0];
console.log('streetNumber', streetNumber) //this works and logs correctly
city = address[1].split(",")[0];
stateAndZip = address[1].split(",")[1];
state = stateAndZip.split(" ")[1];
zip = stateAndZip.split(" ")[2];
})
browser.url('url2.com')
.waitForElementVisible('form')
.setValue('#address-serviceability-form', streetNumber) // this is set to undefined
.pause(5000)
});

Use of indexedDB returns 'undefined'

I'm trying to use indexedDB.
Some parts of my code works.
In the following example, the first function adds server in my DB, however in Chrome debug console there is an undefined message not related to any line. The server is already added though.
The second function puts records in an array, there is also an undefined message not related to any line.
If I do a console.log(servers); just before return servers; I can see the array content, however if I call the function somewhere else in my code, the returned object is undefined.
var dbName = 'myDBname',
dbServersStoreName = 'servers',
dbVersion = 1,
openDBforCreation = indexedDB.open(dbName, dbVersion);
openDBforCreation.onupgradeneeded = function(e) {
var db = e.target.result;
var objStore = db.createObjectStore(dbServersStoreName, { keyPath: "alias"
});
var index = objStore.createIndex("serversAlias", ["alias"]);
};
function addServerInDB(serverAlias,serverAddress,user,pwd){
var myDB = indexedDB.open(dbName, dbVersion);
myDB.onerror = function() {
var notification = document.querySelector('.mdl-js-snackbar');
notification.MaterialSnackbar.showSnackbar(
{message: 'Error while trying to access internal database'});
}
myDB.onsuccess = function(e) {
var db = e.target.result,
request = db.transaction([dbServersStoreName],
"readwrite").objectStore("servers")
.put({alias:''+serverAlias+'',
address:''+serverAddress+'', login:''+user+'',
passwd:''+pwd+''});
request.onsuccess = function(){
var notification = document.querySelector('.mdl-js-snackbar');
notification.MaterialSnackbar.showSnackbar(
{message: 'Server added'});
}
}
};
function listServersInDB(){
var myDB= indexedDB.open(dbName, dbVersion);
myDB.onerror = function() {
var notification = document.querySelector('.mdl-js-snackbar');
notification.MaterialSnackbar.showSnackbar(
{message: 'Error while trying to access internal database'});
}
myDB.onsuccess = function(e) {
var servers = new Array(),
db = e.target.result,
request = db.transaction(["servers"], "readwrite")
.objectStore("servers")
.openCursor();
request.onsuccess = function(e){
var cursor = e.target.result;
if(cursor){
servers.push(cursor.value);
cursor.continue();
}
return servers;
}
}
};
I do not understand where this undefined comes from and if that is why the listServersInDB() function doesn't work.
You need to learn more about how to write asynchronous Javascript. There are too many errors in your code to even begin reasoning about the problem.
Briefly, don't do this:
function open() {
var openDatabaseRequest = ...;
}
openDatabaseRequest.foo = ...;
Instead, do this:
function open() {
var openDatabaseRequest = ...;
openDatabaseRequest.foo = ...;
}
Next, you don't need to try and open the same database multiple times. Why are you calling indexedDB.open twice? You can open a database to both install it and to start using it immediately. All using the same connection.
Next, I'd advise you don't name the database open request as 'myDB'. This is misleading. This is an IDBRequest object, and more specifically, an IDBOpenRequest object. A request isn't a database.
Next, you cannot return the servers array from the request.onsuccess at the end. For one this returns to nowhere and might be source of undefined. Two this returns every single time the cursor is advanced, so it makes no sense at all to return return servers multiple times. Three is that this returns too early, because it cannot return until all servers enumerated. To properly return you need to wait until all servers listed. This means using an asynchronous code pattern. For example, here is how you would do it with a callback:
function listServers(db, callbackFunction) {
var servers = [];
var tx = db.transaction(...);
var store = tx.objectStore(...);
var request = store.openCursor();
request.onsuccess = function() {
var cursor = request.result;
if(cursor) {
servers.push(cursor.value);
cursor.continue();
}
};
tx.oncomplete = function() {
callbackFunction(servers);
};
return 'Requested servers to be loaded ... eventually callback will happen';
}
function connectAndList() {
var request = indexedDB.open(...);
request.onsuccess = function() {
var db = request.result;
listServers(db, onServersListed);
};
}
function onServersListed(servers) {
console.log('Loaded servers array from db:', servers);
}
When you call a function that does not return a value, it returns undefined. All functions in JavaScript return undefined unless you explicitly return something else.
When you call a function from the devtools console, and that function returns undefined, then the console prints out '-> undefined'. This is an ordinary aspect of using the console.
If you want to get a function that returns the list of servers as an array, well, you cannot. The only way to do that in a pretend sort of way, is to use an 'async' function, together with promises.
async function getServers() {
var db = await new Promise(resolve => {
var request = indexedDB.open(...);
request.onsuccess = () => resolve(request.result);
});
var servers = await new Promise(resolve => {
var tx = db.transaction(...);
var request = tx.objectStore(...).getAll();
request.onsuccess = () => resolve(request.result);
});
return servers;
}
One more edit, if you want to call this from the console, use await getServers();. If you do not use the top-level await in console, then you will get the typical return value of async function which is a Promise object. To turn a promise into its return value you must await it.
Clear and helpfull explanations, Thank you.
I open database multiple times beacause the first time is for checking if DB needs an upgrade and doing something if needed. I'll add 'db.close()' in each functions.
Then, I tried your exemple and the result is the same:
console.log('Loaded servers array from db:', servers); works
but return servers; Don't work.
And in console there is already an undefined without related line :
Screenshot

JavaScript - var self = this; self is undefined [duplicate]

This question already has answers here:
How to access the correct `this` inside a callback
(13 answers)
Closed 6 years ago.
so here is a situation that i have. I have a constructor function with many properties and methods. here are the properties:
var Brain = function(){
this.util = require('util');
this.fs = require('fs');
this.assert = require('assert');
this.Sequelize = require('sequelize');
this.JsonField = require('sequelize-json');
this.bcrypt = require('bcrypt-nodejs');
this.bodyParser = require('body-parser');
this.fileUpload = require('express-fileupload');
// Custom Modules
this.ModelsModule = require('./models');
this.TombModule = require('./tomb');
}
a few of the methods are defined to call a given callback and pass data to it, for example:
Brain.prototype.db_read = function(request, response, data, callback) {
var self = this;
self.ModelsModule[data.Model].findOne(data.where)
.then(function(obj) {
// console.log(obj);
callback(request, response, obj);
})
.catch(function(error){
console.log(error);
});
}
Here is the thing - the callback function is always going to be another method of the same constructor function. here is another method that would be a callback:
Brain.prototype.login = function(request, response, user) {
var self = this;
// console.log('form-data --- ', request.body);
if(user == null || user == undefined) {
console.log('Account Load: Failed');
return response.render( self.TombModule.pages['login'], {error: 'Invalid Credentials'} );
}
else {
// console.log(user.dataValues);
if( self.bcrypt.compareSync(request.body.pswrd, user.dataValues.pswrd) == false ) {
console.log('Account Load: Failed');
return response.render( self.TombModule.pages['login'], {error: 'Invalid Credentials'} );
}
console.log('Account Load: Successful');
request.session.you = {
id: user.dataValues.id,
f_name: user.dataValues.f_name,
m_initial: user.dataValues.m_initial,
l_name: user.dataValues.l_name,
icon: user.dataValues.icon,
background: user.dataValues.background,
email: user.dataValues.email,
phone: user.dataValues.phone,
fax: user.dataValues.fax,
uv: user.dataValues.uniqueValue
};
return response.redirect('/home');
}
}
In my app.js a new instance of Brain is created.
const RoutinesModule = require('./routines');
const brain = new RoutinesModule.Brain();
The Entire chain of events starts from an express POST route:
app.post('/login', function(request, response){
var data = {
Model: 'Users',
where: {where: {email: request.body.email}}
}
brain.db_read(request, response, data, brain.login);
});
Notice that the callback, the last parameter, for Brain.db_read is Brain.login, which is another method of the same constructor. here is where the problem is happening.
When POST requests to /login hits the app.js, it's going to query the database and give the results to any given function to handle it, in this case Brain.login
inside of Brain.db_read(), var self = this; works. it points to itself, the instance of the Brain. however, when it calls the callback which is Brain.login, the statement inside of Brain.login, var self = this; does not work. it results in undefined, causing an error.
Why is that happening? why is var self = this; inside of the Brain.login resulting in undefined?
Ultimately what i am trying to do is create a set of main functions to handle database operations(CRUD) instead of doing database operations inside of each individual function, which is/could be an indefinite amount.
I could easily just require that module in that function definition but i would strongly prefer to access its property for dynamic, scalable, and efficiency means.
Thanks!
You're passing the method, brain.login, as a function that has no owner. You're just passing the function reference. You need to do brain.login.bind(brain).

Error assigning value to JavaScript class property

I am getting an error while setting a class property in javascript class. I am using nodejs prompt module to get user input and setting it to the class property. But i am getting following error.
TypeError: Cannot read property 'resultAge' of undefined
I figured it out that it has something to do with synchronization, but i am not able to figure it out that how to implement it for this situation.
Also i want to prompt user again until he has entered a valid number (I can not use a do while loop, what might be the solution?)
var prompt = require("prompt");
var ageTotal = function(){
this.resultAge = 0;
this.getUserAge = function(){
prompt.start();
//i want to run this until valid input is entered
prompt.get(["age"], function(err, result){
//I know i have to convert userInput to int but thats for later
this.resultAge += result.age
});
}
}
ageTotal.prototype.displayTotalAge = function(){
return this.resultAge;
}
var a = new ageTotal();
a.getUserAge();
var age = a.displayTotalAge();
console.log(age); //This is running before the above function finishes
EDIT:
The problem setting resultAge is solved but now the problem is var age = a.displayTotalAge(); is evaluated after console.log(age) which results in 0;
You need to pass the scope of ageTotal into the prompt.get callback:
var ageTotal = function(){
this.resultAge = 0;
this.getUserAge = function(){
var that = this;
prompt.start();
prompt.get(["age"], function(err, result){
that.resultAge += result.age
});
}
}

Returning results from a JavaScript function

I'm fairly new to JavaScript and am hoping someone can help me understand how to modify the function below so it will properly return a result when called. The code currently works and the handleResults function is called once the session string is generated. What I would like to do is modify the generateSessionString function so it will return the session string rather than passing it to handleResults. Can anyone give me suggestions on how I can accomplish this?
function generateSessionString(){
var cb = function (success, results){
if(!success)
alert(results);
if(results.code && results.message){
alert (results.message);
return;
}
handleResults(results);
};
var config = new KalturaConfiguration(gPartnerID);
config.serviceUrl = gServiceURL;
var client = new KalturaClient(config);
var partnerId = gPartnerID;
var userId = gUserName;
var password = gPassWord;
var expiry = gExpiry;
var privileges = gPrivileges;
var result = client.user.login(cb, partnerId, userId, password, expiry, privileges);
return result;
}
function handleResults(ks){
KalturaSessionString = ks;
}
if you like to write it in a sync way(it's still async code) you can try promise(in this example i used jQuery)
function generateSessionString(){
var dfd = new jQuery.Deferred();
var cb = function (success, results){
if(!success)
dfd.fail(results);
if(results.code && results.message){
dfd.fail (results.message);
return;
}
dfd.resolve(results);
};
var config = new KalturaConfiguration(gPartnerID);
config.serviceUrl = gServiceURL;
var client = new KalturaClient(config);
var partnerId = gPartnerID;
var userId = gUserName;
var password = gPassWord;
var expiry = gExpiry;
var privileges = gPrivileges;
client.user.login(cb, partnerId, userId, password, expiry, privileges);
return dfd.promise();
}
$.when(generateSessionString()).then(
function(session)
{
alert(session);
}
)
#Itay Kinnrot's answer is right.Actually,jQuery's on/trigger is another way to solve it,but $.Deferred is better.
if you want to know more about it,you could try to understand Pub/Sub Pattern.
This article is recommended:
http://www.elijahmanor.com/2013/03/angry-birds-of-javascript-blue-bird.html

Categories