Why are these variables not maintaing value? - javascript

I have two problems i cant figure out. When i call GetParams first to get used defined values from a text file, the line of code after it is called first, or is reported to the console before i get data back from the function. Any data gathered in that function is null and void. The variables clearly are being assigned data but after the function call it dissapears.
let udGasPrice = 0;
let udGasLimit = 0;
let udSlippage = 0;
I want to get data from a text file and assign it to variables that need to be global. able to be assigned in a function but used outside it. So above is what i was doing to declare them outside the function. because if i declare them inside, i lose scope. It doesnt seem right to declare with 0 and then reassign, but how else can i declare them gloabaly to be manipulated by another function?
next the code is called for the function to do the work
GetParams();
console.log('udGasPrice = " + udGasPrice );
The code after GetParams is reporting 0 but inside the function the values are right
The data is read and clearly assigned inside the function. its not pretty or clever but it works.
function GetParams()
{
const fs = require('fs')
fs.readFile('./Config.txt', 'utf8' , (err, data) => {
if (err) {
console.error(err)
return;
}
// read file contents into variable to be manipulated
var fcnts = data;
let icnt = 0;
for (var x = 0; x < fcnts.length; x++) {
var c = fcnts.charAt(x);
//find the comma
if (c == ',') {
// found the comma, count it so we know where we are.
icnt++;
if (icnt == 1 ) {
// the first param
udGasPrice = fcnts.slice(0, x);
console.log(`udGasPrice = ` + udGasPrice);
} else if (icnt == 2 ) {
// second param
udGaslimit = fcnts.slice(udGasPrice.length+1, x);
console.log(`udGaslimit = ` + udGaslimit);
} else {
udSlippage = fcnts.slice(udGaslimit.length + udGasPrice.length +2, x);
console.log(`udSlippage = ` + udSlippage );
}
}
}
})
}
Like i said i know the algorithm is poor, but it works.(Im very noob) but why are the variables not retaining value, and why is the code after GetParams() executed first? Thank you for your time.

The code is executed before the GetParams method finishes, because what it does is an asynchronous work. You can see that by the use of a callback function when the file is being read.
As a best practice, you should either provide a callback to GetParams and call it with the results from the file or use a more modern approach by adopting promises and (optionally) async/await syntax.

fs.readFile asynchronously reads the entire contents of a file. So your console.log('udGasPrice = " + udGasPrice ); won't wait for GetParams function.
Possible resolutions are:
Use callback or promise
let udGasPrice = 0;
let udGasLimit = 0;
let udSlippage = 0;
GetParams(() => {
console.log("udGasPrice = " + udGasPrice);
});
function GetParams(callback) {
const fs = require('fs')
fs.readFile('./Config.txt', 'utf8', (err, data) => {
if (err) {
console.error(err)
return;
}
// read file contents into variable to be manipulated
var fcnts = data;
let icnt = 0;
for (var x = 0; x < fcnts.length; x++) {
var c = fcnts.charAt(x);
//find the comma
if (c == ',') {
// found the comma, count it so we know where we are.
icnt++;
if (icnt == 1) {
// the first param
udGasPrice = fcnts.slice(0, x);
console.log(`udGasPrice = ` + udGasPrice);
} else if (icnt == 2) {
// second param
udGaslimit = fcnts.slice(udGasPrice.length + 1, x);
console.log(`udGaslimit = ` + udGaslimit);
} else {
udSlippage = fcnts.slice(udGaslimit.length + udGasPrice.length + 2, x);
console.log(`udSlippage = ` + udSlippage);
}
}
}
callback()
})
}
fs.readFileSync(path[, options]) - it perform same operation in sync - you still need to edit your code accordingly
Also, it's advisable that you don't edit global variables in the function and return updated variables from the function.

Related

For-loop order (with asynchronous function)

I have this code :
exports.formatDepArrTables = function(jsonReturn, type, placeName, callback) {
let toSend = "";
if (type == 'departure') {
for (let j = 0; j < jsonReturn.departures.length; j++) {
console.log("DEPARTURES LOOP j = " + j + "/" + jsonReturn.departures.length);
if(currentDeparture.display_informations.links[0] === undefined) {
toSend += ""; // setting some string informations
}
else {
let oceTrainId = ""; // random number given by a json
_this.getDisruptionFromDisruptionId(oceTrainId, function(data) {
for(let i = 0; i < data.disruptions[0].impacted_objects[0].impacted_stops.length; i++) {
if(currentImpactedStop.stop_point.label == placeName) {
toSend += "string";
}
}
});
}
}
console.log("End of the first loop");
return callback(toSend);
} else if (type == 'arrival') {
// copy&paste
} else {
throw new Error("not defined");
}
};
When I run this code (I use NodeJS), it makes 1/10, 2/10... from the first loop but it didn't iterate the second loop on the 1st iteration of the first loop (and it shows "End of the first loop" and starts the second loop).
getDisruptionFromDisruptionId is a custom method which makes a request (with 'request' NodeJS module) on an API.
Of course, I need to have informations given by getDisruptionFromDisruptionId to run my next loop...
Parent function of this code part is returning a callback that needs to be "filled" at the end of the both two loops.
Any ideas ?
request is async function, you need to add async / await to your code or use recursion

learnyounode exercise 9 - output is showing undefined, problems understanding async or the logic

I've been struggling with the JUGGLING ASYNC task on learnyounode. As far as I can tell, I'm doing things almost right and it looks like the issue sits somewhere with the async compounded by myself trying to avoid bl or other modules. I did the previous task without another module and would like to continue this trend.
const http = require('http');
const url = process.argv[2];
let content = [];
let count = 0;
const arguments = process.argv;
let urlArray = arguments.filter(function pullUrls(element, index, array) {
return index >= 2;
});
function printResults() {
for (var i = 0; i < 3; i++) {
console.log(content[i]);
}
}
function httpGet(index) {
http.get(urlArray[index], function(response){
response.on('data', function(data){
newData = data.toString();
content[index] = content[index] + newData;
})
response.on('end', function(){
count++;
if (count === 3){
printResults();
}
})
})
}
for (var i = 0; i < 3; i++) {
httpGet(i);
}
Common output is very close to what is expected, but includes an undefined string at the beginning and I can't figure out why.
undefinedLets get some ute when we're going parma. Grab us a slab how it'll be bull bar. He's got a massive bushie where stands out like a pot.
undefinedShe'll be right mongrel heaps as cross as a hit the turps. Stands out like a booze also you little ripper flick. As stands out like ironman when lets throw a bikkie.
undefinedHe hasn't got a bounce with gutful of struth. Stands out like a aerial pingpong piece of piss built like a battler.
As you can see, it's finding the array 'undefined' first, which is true, but then appending onto it.
My best guess is that it's the content[index] = content[index] + newData; line somehow holding on to the undefined nature of let content = [] before content[i] is figured out. Now I've written it out and thought it through, this might be a simple JS problem I'm just overlooking or don't-know-that-I-don't-know.
Any help would be good.
Your getting right output, content[index] is undefined initially, you could do a undefined check, before concatenating newData. Here is complete code with changes.
const http = require('http');
const url = process.argv[2];
let content = [];
let count = 0;
const arguments = process.argv;
let urlArray = arguments.filter(function pullUrls(element, index, array) {
return index >= 2;
});
function printResults() {
for (var i = 0; i < 3; i++) {
console.log(content[i]);
}
}
function httpGet(index) {
http.get(urlArray[index], function(response){
response.on('data', function(data){
let newData = data.toString();
//check if undefined
if(typeof content[index] !== 'undefined'){
content[index] = content[index] + newData;
}else{
//add newData if undefined
content[index] = newData;
}
})
response.on('end', function(){
count++;
if (count === 3){
printResults();
}
})
})
}
for (var i = 0; i < 3; i++) {
httpGet(i);
}

how to get incremented value in for loop after callback function in javascript?

My Requirement:
I want to get the list of values using for loops. In for loop one iteration completed one time then the callback will send that list of values(array).
Once the first iteration completed second time loop value should be get incremented value.
For example : 5 values
after 5th iteration then loop is over. then second time loop should start with '0' but here it's starting with last incremented value. please help me to achieve this.
Below code is working fine for the first time.
Callback function:
$inventoryManagement.getObjectNameAndAttributeAndDataTypeIdUsingObjectAndAttributeId(objectId,attributeId, function(objectAttributeBlockElement) {
//$scope.val = myOwnJ;
console.log(objectAttributeBlockElement);
});
Function:
var myOwnJ = 0;
// Getting ObjectId And AttributeId Using CellId For Normal Controls
var getObjectNameAndAttributeAndDataTypeIdUsingObjectAndAttributeId = function(objectId,attributeId, callback) {
var objectAttributeBlockElement = [];// one array
try {
// iterate over the objectAttributes
for (var i = 0; i < pageObject.objects.length; i++) {
if (pageObject.objects[i].id == objectId) {
var name = "";
var labelName = "";
var dataTypeId = "";
for (;myOwnJ < pageObject.objects[i].objectAttribute.length;) {
name = pageObject.objects[i].objectAttribute[myOwnJ].name;// got the current label name
labelName = pageObject.objects[i].objectAttribute[myOwnJ].labelName;// got the current name
dataTypeId = pageObject.objects[i].objectAttribute[myOwnJ].dataTypeId;// got the current dataTypeId
objectAttributeBlockElement.push(name,labelName,dataTypeId);
callback(objectAttributeBlockElement, myOwnJ++);
return;
}
}
}
throw {
message: "objectId not found: " + objectId
};
} catch (e) {
console.log(e.message + " in getObjectNameAndAttributeAndDataTypeIdUsingObjectAndAttributeId");
}
};
You could pass j as an additional function parameter, such as
var getObjectNameAndAttributeAndDataTypeIdUsingObjectAndAttributeId = function(objectId, attributeId, j, callback) {
so it won't be a local variable. Then, instead of declaring it locally, use the following:
for (j = ((j === null) ? 0 : j); j < pageObject.objects[i].objectAttribute.length; j++) {
That way, if you call your function with j, you'll get it incremented after each call.
Another approach, which I won't recommend, would be making j a global variable by declaring it ouside your function instead of passing it as a parameter. That way you don't have to modify your function declaration at all. If you're up to that, I strongly suggest modifying the variable name cause j would be too generic for a global scope variable and it will cause trouble sooner or later: use something like myOwnJ and you'll be fine.
EDIT: Full source code (as requested by the OP):
var myOwnJ = 0;
// Getting ObjectId And AttributeId Using CellId For Normal Controls
var getObjectNameAndAttributeAndDataTypeIdUsingObjectAndAttributeId = function(objectId,attributeId, callback) {
var objectAttributeBlockElement = [];// one array
try {
// iterate over the objectAttributes
for (var i = 0; i < pageObject.objects.length; i++) {
if (pageObject.objects[i].id == objectId) {
var name = "";
var labelName = "";
var dataTypeId = "";
if(myOwnJ < pageObject.objects[i].objectAttribute.length) {
name = pageObject.objects[i].objectAttribute[myOwnJ].name;// got the current label name
labelName = pageObject.objects[i].objectAttribute[myOwnJ].labelName;// got the current name
dataTypeId = pageObject.objects[i].objectAttribute[myOwnJ].dataTypeId;// got the current dataTypeId
objectAttributeBlockElement.push(name,labelName,dataTypeId);
callback(objectAttributeBlockElement, myOwnJ++);
return;
}
else {
myOwnJ = 0;
}
}
}
throw {
message: "objectId not found: " + objectId
};
} catch (e) {
console.log(e.message + " in getObjectNameAndAttributeAndDataTypeIdUsingObjectAndAttributeId");
}
};
What you are looking for is a global variable for 'j'. Although this is discouraged to be used.
var j=0;
var getObjectNameAndAttributeAndDataTypeIdUsingObjectAndAttributeId =
function(objectId, attributeId, callback) {
//do your stuff
//increment j
j++;
}

Multidim json array javascript, how to grab values

I have this data response from an AJAX call:
{"18:00":{"twopersons":1,"fourpersons":0}}
Which gets stored into a variable by statsarray = data;
Now how can i loop through statsarray and output the twopersons value?
So I can alert:
18:00 - There's 2 x 2persons and 0 x 4persons
Here is the Ajax call:
var statsarray;
var currentloopeddate = test_date.toString('yyyy-MM-dd')
$.post("/home/sessions",
{ action: 'partner_calendar_checkseats', date: currentloopeddate },
function(data) { statsarray = data; }
);
Just do the following:
var twopersons = data["18:00"].twopersons;
var fourpersons = data["18:00"]["fourpersons"];
(Both variants are possible)
A variant would be:
var shorter = data["18:00"];
var twopersons = data.twopersons;
// ...
Something like:
var tst = {"18:00":{"twopersons":1,"fourpersons":0}};
for(k in tst) {
for(var z in tst[k]) {
console.log(k + ": Theres "+tst[k][z] + " X " + z);
}
}
You can try something like this:
(UPDATE: better example)
var statsarray = {"18:00":{"twopersons":1,"fourpersons":0}};
var hour, persons, line, array;
for (hour in statsarray) {
if (statsarray.hasOwnProperty(hour)) {
array = [];
for (persons in statsarray[hour]) {
if (statsarray[hour].hasOwnProperty(persons)) {
array.push(statsarray[hour][persons] + " x " + persons);
}
}
line = hour + " - There's " + array.join(' and ');
alert(line);
}
}
See: DEMO.
Unfortunately you have to test with .hasOwnProperty to make sure it will work with some libraries.
UPDATE: You have added the code from your AJAX call in your question and I noticed that you declare the statsarray variable outside the callback function, but assign some value to that variable inside the callback. Just keep in mind that you have to run your iteration code inside the function that is the AJAX callback, where you have: statsarray = data; - just after this line, to make sure that you actually have some values to iterate over.

Chrome JavaScript developer console: Is it possible to call console.log() without a newline?

I'd like to use console.log() to log messages without appending a new line after each call to console.log(). Is this possible?
No, it's not possible. You'll have to keep a string and concatenate if you want it all in one line, or put your output elsewhere (say, another window).
In NodeJS you can use process.stdout.write and you can add '\n' if you want.
console.log(msg) is equivalent to process.stdout.write(msg + '\n').
Yes, it's possible (check out the demo below) -- by implementing your own virtual console on top of the native browser console, then syncing it to the real one.
This is much easier than it sounds:
maintain a display buffer (e.g. an array of strings representing one line each)
call console.clear() before writing to erase any previous contents
call console.log() (or warn, error, etc) to fill the console with the contents from your display buffer
Actually, I've been doing this for some time now. A short, rudimentary implementation of the idea would be something along the following lines, but still capable of animating the console contents:
// =================================================
// Rudimentary implementation of a virtual console.
// =================================================
var virtualConsole = {
lines: [],
currentLine: 0,
log: function (msg, appendToCurrentLine) {
if (!appendToCurrentLine) virtualConsole.currentLine++;
if (appendToCurrentLine && virtualConsole.lines[virtualConsole.currentLine]) {
virtualConsole.lines[virtualConsole.currentLine] += msg;
} else {
virtualConsole.lines[virtualConsole.currentLine] = msg;
}
console.clear();
virtualConsole.lines.forEach(function (line) {
console.log(line);
});
},
clear: function () {
console.clear();
virtualConsole.currentLine = 0;
}
}
// =================================================
// Little demo to demonstrate how it looks.
// =================================================
// Write an initial console entry.
virtualConsole.log("Loading");
// Append to last line a few times.
var loadIndicatorInterval = setInterval(function () {
virtualConsole.log(".", true); // <- Append.
}, 500);
// Write a new line.
setTimeout(function () {
clearInterval(loadIndicatorInterval);
virtualConsole.log("Finished."); // <- New line.
}, 8000);
It sure has its drawbacks when mixing with direct console interaction, and can definitely look ugly -- but it certainly has its valid uses, which you couldn't achieve without it.
You can put as many things in arguments as you'd like:
console.log('hi','these','words','will','be','separated','by','spaces',window,document)
You'll get all that output on one line with the object references inline and you can then drop down their inspectors from there.
The short answer is no.
But
If your use-case involves attempting to log perpetually changing data while avoiding console-bloat, then one way to achieve this (in certain browsers) would be to use console.clear() before each output.
function writeSingleLine (msg) {
console.clear();
console.log(msg);
}
writeSingleLine('this');
setTimeout( function () { writeSingleLine('is'); }, 1000);
setTimeout( function () { writeSingleLine('a'); }, 2000);
setTimeout( function () { writeSingleLine('hack'); }, 3000);
Note that this would probably break any other logging functionality that was taking place within your application.
Disclaimer: I would class this as a hack.
collect your output in an array and then use join function with a preferred separator
function echo(name, num){
var ar= [];
for(var i =0;i<num;i++){
ar.push(name);
}
console.log(ar.join(', '));
}
echo("apple",3)
check also Array.prototype.join() for mode details
var elements = ['Fire', 'Wind', 'Rain'];
console.log(elements.join());
// expected output: Fire,Wind,Rain
console.log(elements.join(''));
// expected output: FireWindRain
console.log(elements.join('-'));
// expected output: Fire-Wind-Rain
If your only purpose to stop printing on many lines, One way is to group the values if you don't want them to fill your complete console
P.S.:- See you browser console for output
let arr = new Array(10).fill(0)
console.groupCollapsed('index')
arr.forEach((val,index) => {
console.log(index)
})
console.groupEnd()
console.group
console.groupCollapsed
Something about #shennan idea:
function init(poolSize) {
var pool = [];
console._log = console.log;
console.log = function log() {
pool.push(arguments);
while (pool.length > poolSize) pool.shift();
draw();
}
console.toLast = function toLast() {
while (pool.length > poolSize) pool.shift();
var last = pool.pop() || [];
for (var a = 0; a < arguments.length; a++) {
last[last.length++] = arguments[a];
}
pool.push(last);
draw();
}
function draw() {
console.clear();
for(var i = 0; i < pool.length; i++)
console._log.apply(console, pool[i]);
}
}
function restore() {
console.log = console._log;
delete console._log;
delete console.toLast;
}
init(3);
console.log(1);
console.log(2);
console.log(3);
console.log(4); // 1 will disappeared here
console.toLast(5); // 5 will go to row with 4
restore();
A simple solution using buffered output. Works with deno and should work with node.js. (built for porting pascal console programs to javascript)
const write = (function(){
let buffer = '';
return function (text='\n') {
buffer += text;
let chunks = buffer.split('\n');
buffer = chunks.pop();
for (let chunk of chunks)
{console.log(chunk);}
}
})();
function writeln(text) { write(text + '\n'); }
To flush the buffer, you should call write() at the end of program.
If you mix this with console.log calls, you may get garbage output.
if you want for example console log array elements without a newline you can do like this
const arr = [1,2,3,4,5];
Array.prototype.log = (sep='') => {
let res = '';
for(let j=0; j<this.lengthl j++){
res += this[j];
res += sep;
}
console.log(res);
}
// console loging
arr.log(sep=' '); // result is: 1 2 3 4 5
Useful for debugging or learning what long chained maps are actually doing.
let myConsole = (function(){
let the_log_buffer=[[]], the_count=0, the_single_line=false;
const THE_CONSOLE=console, LINE_DIVIDER=' ~ ', ONE_LINE='ONE_LINE',
PARAMETER_SEPARATOR= ', ', NEW_LINE = Symbol();
const start = (line_type='NOT_ONE_LINE') => {
the_log_buffer=[[]];
the_count=0;
the_single_line = line_type == ONE_LINE;
console = myConsole;
}
const stop = () => {
isNewline();
console = THE_CONSOLE;
};
const isNewline = a_param => {
if (the_single_line && a_param==NEW_LINE) return;
const buffer_parts = the_log_buffer.map(one_set=> one_set.join(PARAMETER_SEPARATOR))
const buffer_line = buffer_parts.join(LINE_DIVIDER);
if (the_single_line) {
THE_CONSOLE.clear();
}
THE_CONSOLE.log( buffer_line );
the_log_buffer = [[]];
the_count=0;
}
const anObject = an_object => {
if (an_object instanceof Error){
const error_props = [...Object.getOwnPropertyNames(an_object)];
error_props.map( error_key => an_object['_' + error_key] = an_object[error_key] );
}
the_log_buffer[the_count].push(JSON.stringify(an_object));
}
const aScalar = a_scalar => {
if (typeof a_scalar === 'string' && !isNaN(a_scalar)) {
the_log_buffer[the_count].push("'" + a_scalar + "'");
} else {
the_log_buffer[the_count].push(a_scalar);
}
}
const notNewline = a_param => typeof a_param === 'object' ? anObject(a_param):aScalar(a_param);
const checkNewline = a_param => a_param == NEW_LINE ? isNewline(a_param) : notNewline(a_param);
const log = (...parameters_list) => {
the_log_buffer[the_count]=[];
parameters_list.map( checkNewline );
if (the_single_line){
isNewline(undefined);
}else{
const last_log = parameters_list.pop();
if (last_log !== NEW_LINE){
the_count++;
}
}
}
return Object.assign({}, console, {start, stop, log, ONE_LINE, NEW_LINE});
})();
function showConcatLog(){
myConsole.stop();
myConsole.start();
console.log('a');
console.log('bb');
console.dir({i:'not', j:'affected', k:'but not in step'})
console.log('ccc');
console.log([1,2,3,4,5,'6'], {x:8, y:'9'});
console.log("dddd", 1, '2', 3, myConsole.NEW_LINE);
console.log("z", myConsole.NEW_LINE, 8, '7');
console.log(new Error("error test"));
myConsole.stop();
}
myConsole.start(myConsole.ONE_LINE);
var stop_callback = 5;
function myCallback(){
console.log(stop_callback, 'Date.now()', myConsole.NEW_LINE, Date.now());
stop_callback--;
if (stop_callback>0){
window.setTimeout(myCallback, 1000);
}else{
showConcatLog();
}
}
window.setTimeout(myCallback, 1000);
You can use a spread operator to display output in the single line. The new feature of javascript ES6. see below example
for(let i = 1; i<=10; i++){
let arrData = [];
for(let j = 1; j<= 10; j++){
arrData.push(j+"X"+i+"="+(j*i));
}
console.log(...arrData);
}
That will print 1 to 10 table in single line.
// Source code for printing 2d array
window.onload = function () {
var A = [[1, 2], [3, 4]];
Print(A);
}
function Print(A) {
var rows = A.length;
var cols = A[0].length;
var line = "";
for (var r = 0; r < rows; r++) {
line = "";
for (var c = 0; c < cols; c++) {
line += A[r][c] + " ";
}
console.log(line);
}
}

Categories