Possible Node.js memory leak when using callbacks - javascript

I've been coding with Node.js, but since I'm not that familiar with how GC works with async methods, I believe I created a memory leak in this code:
function getPriceCC(coins, chn) {
// Get the spot price of the pair and send it to general
cc.priceFull(coins.map(function(c){return c.toUpperCase();}),['USD', 'EUR']).
then(prices => {
var msg = '__**CryptoCompare**__\n';
for(var i = 0; i < coins.length; i++)
msg += ('- **' + coins[i].toUpperCase() + '-USD** is : `' +
prices[coins[i].toUpperCase()]['USD']['PRICE'] + ' USD` (`' +
Math.round(prices[coins[i].toUpperCase()]['USD']['CHANGEPCT24HOUR']*100)/100 + '%`).\n'
);
chn.send(msg);
delete chn;
delete coins;
delete msg;
delete prices;
})
.catch(console.error);
}
I read about closure, so I tried deleting the variables inside the callback function, but it keeps stacking up. I've also found that some people dislike the use of var and say my code is horrible, while some example code from Google uses var.
If any clarification is needed or extra code required, please do tell.
Note: Node version is 8.0.0, and the instance is Ubuntu 16.04 on AWS.

Related

Is there a better way of sending messages to specific channels in specific servers?

I'm trying to make a logs script, however, I can not get it to work. Is there a better way?
I've tried switching around the properties, but it doesn't seem to work.
client.on('message', message => {
var sender = message.member.user.tag
var channel = message.channel.name
var message = message.content
var server = message.guild
let embed = new Discord.RichEmbed()
.addField('**' + sender + '** said ' + message + ' in ' + channel + ', ' + server)
.setColor('#4286f4')
// This is the line I'm having problems with:
client.guilds.get('575957043211403265').channels.get('575957043211403267').sendEmbed(embed);
});
If the IDs you're using are correct, then your code should be valid, as long as you're using the library from the stable branch.
You're using the TextChannel.sendEmbed() method, which is deprecated: that means that it'll be removed in the future versions. If you installed the master branch that method won't be available (check the docs here).
There is another way of sending embeds:
TextChannel.send({ embed: yourEmbed });
In your case, it would be:
client.guilds.get('575957043211403265').channels.get('575957043211403267').send({ embed });
If that doesn't work, please include the error in your reply, you might have missed something in the question :)

Run Node.js file on button click event

In my current project, we have a HTML page. In HTML page, we have several buttons, for instance we have buttons for Temperature Sensor, Humidity Sensor, Alarm etc. When we click on a button than in back-end it will run corresponding Node.js file, for instance when we click on Temperature sensor button than it will run TemperatureSensor.js file located in the same path. The code for HTML page is as shown below:
The code of TemperatureSensor.js is as below:
var mqtt = require('mqtt');
var client = mqtt.connect('mqtt://test.mosquitto.org:1883');
var NUM_SAMPLE_FOR_AVG = 5;
var numSample = 0;
var tempCelcius = 0;
var currentAvg = 0;
client.subscribe('tempMeasurement');
client.on('message', function(topic, payload) {
if (topic.toString() == "tempMeasurement") {
sensorMeasurement = JSON.parse(payload);
console.log("tempValue is " + sensorMeasurement.tempValue);
if (numSample <= NUM_SAMPLE_FOR_AVG) {
numSample = numSample + 1;
if (sensorMeasurement.unitOfMeasurement == 'F') {
tempCelcius = ((sensorMeasurement.tempValue - 32) * (5 / 9));
} else {
tempCelcius = sensorMeasurement.tempValue;
}
currentAvg = parseFloat(currentAvg) + parseFloat(tempCelcius);
if (numSample == NUM_SAMPLE_FOR_AVG) {
currentAvg = currentAvg / NUM_SAMPLE_FOR_AVG;
var avgTemp = {
"tempValue" : parseFloat(currentAvg),
"unitOfMeasurement" : sensorMeasurement.unitOfMeasurement
};
client.publish('roomAvgTempMeasurement', JSON
.stringify(avgTemp));
console.log("Publishing Data roomAvgTempMeasurement ");
numSample = 0;
currentAvg = 0;
}
}
}
});
The problem is when we clicked on TemperatureSensor button in browser than it display error: TemperatureSensor.js:1 Uncaught ReferenceError: require is not defined. if the content of TemeperatureSensor is console.log("Hello") than it displays Hello in the console of browser. How to provide dependency ??Why we need to do this bcoz if we want to run TemperatureSensor, HumiditySensor etc. than we need to run these in terminal, for instance if we want to run TemperatureSensor than in terminal we have to write sudo node TempeatureSensor.js. This require more manual efforts so in order to reduce this effort we need such kind of HTML page. How to resolve the about problem ??
You can't run Node.js code in the browser, they're completely separate environments (for example, browsers do not have the require function, hence why you're getting that error). Your best bet is to look into creating a REST service of some kind (using Express, Hapi or Restify, most likely) that will allow you to call a Node.js server through HTTP.
This is a decent introduction to the topic - it uses MongoDB for data persistence, but this is in no way a requirement when it comes to making stuff like this. In your case, you'll basically just have to define a route for Temp and Humidity, run your code to get the data in the route handler, and then send JSON data back on the response object. You'll then be able to use jQuery (or any number of other libraries) to make AJAX requests to these routes.
EDIT: After looking at the MQTT GitHub page, there is another option - the library can be used in the browser if bundled using a tool like Browserify or Webpack. Given the complexities of learning to write and maintain REST services, this may well be a better option.

Streaming images with Nodejs on embedded device

I'm trying to stream images from a Node.js server to a client site. I've started with socket.io, but my implementation is fairly memory intensive (and possibly leaking as I'm not fluent in JavaScript). I'm just prototyping at this point with 10 sample images:
Server Side
Responds to a socket.io trigger with the following function that "streams" 10 images to the client at roughly 100ms intervals.
socket.on('img_trigger', function(data) {
var img_num = 0;
var timeoutHandle = null;
function startTimeout() {
stopTimeout();
if (img_num < 10) {
timeoutHandle = setTimeout(updateStream, 100);
}
}
function stopTimeout() {
clearTimeout(timeoutHandle);
}
function updateStream() {
var file = './sampleframes/sample-' + img_num + '.png';
fs.readFile(file , function(err, file_buff) {
if (err !== null) {
console.log('readFile error: ' + err);
} else {
socket.emit('img_stream', { buffer: file_buff });
}
file_buff = null;
++img_num;
});
startTimeout();
}
// kicks off first image
startTimeout();
});
Client Side
Capture the raw buffer data and generate a PNG with an <img> element.
socket.on('img_stream', function(data) {
var img_data = arrayBufferToBase64(data.buffer);
var panel = $('#frame-panel');
$('#frame').html('<img src="data:image/png;base64,' +
img_data + '" width="' + panel.width() + '" height="' +
panel.height() + '" />');
});
If I trigger the server once, it works fine but not great. I notice the memory usage go up significantly, and it crashes after several triggers. Can I improve my code here to be efficient or should I try a new approach?
I've looked into using Node's File Streams, socket.io-streams, and even Binary.js (though I hesitate to require our clients to have too-modern of browsers) and they look promising, but I don't quite know which would be best for my use-case. Any help or guidance would be greatly appreciated.
The web interface I'm developing is for an FPGA (Zynq-7000) based camera running PetaLinux with Node.js cross-compiled for the ARM processor, so I don't have a lot of server-side resources to work with. As such, I'd like to have the client-side do as much of the processing as possible. Eventually, streaming video would be incredible, but I'd be satisfied with reading and displaying successive frames at a reasonable rate.
This may be due to a memory leak within the socket.io library (see here for a description, and here for a proposed fix).
To fix this, download and use the latest version of socket.io from here.

Changing EventSource in HTML

Basically, what I'm trying to do is to pass a parameter through the URL to the php code, however, it seems that in the function body of the on message event, I can't change the source. Here's the code below:
var source = new EventSource("get_message.php?latest_chat_ID=0");
var i = 0;
$(source).on("message", function (event) {
var data = event.originalEvent.data;
++i;
source = new EventSource("get_message.php?latest_chat_ID=" + i);
// $.post("get_message.php", {latest_chat_ID: 0}, function (data, status) {});
$("#messages").html($("#messages").html() + data);
});
I was wondering -
How do I rectify this problem?
Are there other ways to send data to a PHP page? (I contemplated using the $.post{} jQuery function, but that will execute the script twice - once from firing the EventSource event and once from the .post{} request?)
I also understand that alternative technologies exist, such as WebSockets and libraries such as node.js, that are better suited for bidirectional communication. However, most of my base code is written with an SSE implementation in mind, and I'd like to stick to that.
If you want to continue using SSE, I think you'll need to rewrite what you have similar to what is below. The reason what you have right now doesn't work is because you are only listening to the first EventSource, and just changing the variable. The variable is not reused by jQuery when you change it. Plus, I probably would skip using jQuery for that since it's going to try and cache things you don't want cached.
var listenToServer = function(i) {
source = new EventSource("get_message.php?latest_chat_ID=" + i);
source.onmessage = function(event) {
var data = event.originalEvent.data;
$messages.html($messages.html() + data);
listenToServer(i + 1);
}
},
$messages = $('#messages'),
source;
listenToServer(0);
I also went ahead and cached $('#messages') so you're not creating new objects over and over. Left source outside of the function so that you don't have to worry as much about garbage collection with the various EventSources.

Update DOM Elements Immediately in Javascript

For a simple project, I'm trying to display log output in a textarea. I have a log function, and would like the textarea to always be updated immediately whenever log is called. Unfortunately, it seems to only do the update once, and push everything at the same time (I'm using JQuery to set the value of the textarea, if that possibly matters). Here's a basic example:
for (i = 0; i <= 100; i++) {
$("textarea").html($("textarea").html() + "test" + i + "\n");
$("textarea").scrollTop(9999999)
};
This spits out all the text into the textarea at once (note: you can see the results of these examples at this jsfiddle). This basic example is easily remedied by creating a timeout and using recursive function calls:
f = function(i) {
if (i <= 100) {
$("textarea").html($("textarea").html() + "test" + i + "\n");
$("textarea").scrollTop(999999);
setTimeout(function() { f(i+1); }, 0);
}
};
f(1);
This version spits out the text into the textarea one line at a time, which is what I want. But using timeouts and callbacks in this manner does not seem practical in the setting of logging; every time I call log, I would have to provide a callback for all the functionality that I want to ever follow the log call.
Is there any way to achieve the desired effect without callbacks?
I think you might consider using :
$("textarea").val(function(index, value) {
return value + "test" + i + "\n"
});
instead of :
$("textarea").html($("textarea").html() + "test" + i + "\n");
or in general :
$("textarea").val(NEWVAL)
Also noted in the comments of your question, if you want to be able to notice "by eye" all the messages that arrives you'll have to save them in a buffer and have something like (not tested) :
var buffer = []
function log(text) { buffer.push(text) }
setInterval(function(){
if (len(buffer)>0) {
$("textarea").val(function(index, value) {
return value + "\n" + buffer.shift()
});
}
},500)
Browsers generally (Opera is an exception) do JS execution and rendering on the same thread, so while your JS is running there will be no rendering done by th browser, unless you explicitly yield control via a timeout or interval timer.

Categories

Resources