I have an asynchronous function running in my web application that enables a chat input. In a different component I need to set a variable to the input and then focus() on it when certain conditionals are met. Unfortunately the chat input DOM element isn't always available when I try to declare it based on the asynchronous nature of the function that enables it. Being familiar with how setTimeoout() works with the call stack I wrapped my declaration in a setTimeout and everything (seemingly) works as expected now.
So my question is if this is a good practice or not? I'm using React/Redux and will have to do a lot of prop threading and extra logic to get a seemingly easy task accomplished without the setTimeout.
It is an alright practice ;)
It gets the job done, but it is usually preferable to work with callbacks or promises instead of polling to see if the dom is ready. The main failing with a "setTimeout" approach is that you are setting a timer and what if the resource (chat plugin) takes longer to load than the timer you set.
// Run it
main();
// Supporting code
function main() {
let attempts = 0;
const maxAttempts = 10;
tryUpdate();
function tryUpdate() {
// Call it once
attempts++;
const success = updateAndFocus();
console.log(attempts);
// Keep calling it every 100ms
if (!success && attempts < maxAttempts) {
setTimeout(() => tryUpdate(), 100);
}
}
}
function updateAndFocus() {
const el = document.getElementById('findme');
if (!el) return false;
// do work
el.focus;
return true;
}
say I have a long task that starts running when a person connects to InitializeDB. (Of course with authorization in the future, but left that out for now).
'post /initializeDB':'OrderController.initializeAll',
Now the problem is: the initialize function should never be run twice. - I know ideally I set up a taskmanager which just starts a task in the background which I could poll.
However for current simplicity, (and to show a proof of concept) is it possible for a sails route to "know" that another connection/route is already running? So that if I connect twice to /initializeDB it won't try to initialize the database twice?
You can use a variable in your controller - just toggle it to true when the process is running, something like that. So, in OrderController.js:
var initializeRunning = false;
module.exports = {
initializeAll: function(req, res) {
// return benign result if already running
if (initializeRunning) {
return res.send({alreadyRunning: true});
}
// start running
initializeRunning = true;
// using setTimeout as a stand-in for a long async process
setTimeout(function() {
// finished the process
res.send({complete: true});
// if you want to allow this method to run again later, unset your toggle
initializeRunning = false;
}, 3000);
},
};
Can anyone tell me how to send continuous updates to connected clients every second using nodejs and socket.io?
NOTE: I don't want to use the setInterval() function as it is unfit for my current scenario.
You can do this with setTimeout in a function that references itself in the setTimeout. Basically the same result as doing setInterval but will always wait for the function to finish (assuming synchronous code) before running the timeout function again.
function thingToRepeat() {
let shouldCancel = false;
// send messages, do stuff,
// set shouldCancel to true to stop looping if needed
if (!shouldCancel) {
setTimeout(thingToRepeat, 1000);
}
}
I'm a little bit newbie with Nodejs.
I'm working in a Nodejs - express solution.
I want to send and e-mail when some information is added to a MSSSQL database.
This is working well for me. The problem is that I want to check every five minutes that this information added to the database is modified or not, and if not, send another e-mail.
The call to add information to the db is this route:
router.post('/postlinevalidation', function(req, res) {
//insert function into mssql
silkcartCtrl.sendMail(req, res);
});
The controller part for sending the e-mail:
exports.sendMail = function(req, res) {
var emails = "";
fs.readFile('./config/email.conf', 'utf8', function (err,data) {
if (err) {
return logger.error(err);
}
emails = data;
});
var minutes = 5, the_interval = minutes * 60 * 1000;
var refreshId = setInterval(function() {
logger.info("I am doing my 5 minutes check FL_PENDIENTE");
var request = new sql.Request(req.dbsqlserver);
var sqlpendinglinesvalidation = "SELECT [FK_IDCHECK],[FK_IDPEDIDO],[BK_IDPROVEEDOR],[DE_PROVEEDOR]"+
",[FK_FAMILIA],[BK_FAMILIA],[FK_SUBFAMILIA],[BK_SUBFAMILIA],[FK_ARTICULO]"+
",[BK_ARTICULO],[FL_VALIDAR],[DT_FECHA],[FL_PENDIENTE],[DES_CHECK],[QNT_PROPUESTA],[FECHA]"+
"FROM TABLE"+
" WHERE [FL_PENDIENTE] = 1";
request.query(sqlpendinglinesvalidation, function (err, recordset) {
if (recordset.length > 0) {
var transporter = nodemailer.createTransport('smtps://user%40gmail.com:pwd#smtp.gmail.com');
var mailOptions = {
from: '"Mailer" <mail#mail.com>', // sender address
to: emails, // list of receivers
subject: 'Tienes compras pendientes de validar', // Subject line
text: 'Tienes compras pendientes de validar', // plaintext body
html: '<b>Tienes compras pendientes de validar.</b>' // html body
};
// send mail with defined transport object
transporter.sendMail(mailOptions, function(error, info){
if(error){
return logger.error(error);
}
logger.info('Message sent: ' + info.response);
});
} else {
clearInterval(refreshId);
return true;
}
});
}, the_interval);
};
As I said this is working well.
I control the five minutes withsetInterval
But I supossed every time the route postlinevalidation is called, a new thread is open, so I will have several setInterval processes running.
I want to know how to manage it. If the controller function exports.sendMail is running, when the route is called again, "kill this process", and start again exports.sendMail
Thanks in advance
But I supossed every time the route postlinevalidation is called, a
new thread is open, so I will have several setInterval processes
running.
No, this is not how node.js works. You don't get multiple threads because of multiple setInterval() timers.
node.js by itself is single threaded. So, each time a route is called, it just creates an event in the node.js event queue and they are served FIFO, one at a time. At any point that one of the route handlers makes an async call, it essentially "yields" control back and the next item in the event queue gets to run until it yields or finishes.
Timers like setInterval() also use the event queue so no additional threads are creates by setInterval(). It is possible that node.js modules that use native code may themselves use threads and node.js uses a small thread pool that it uses for disk managemnet, but neither of those have anything to do with setInterval().
If you explicitly want to create another execution context for a long running operation in node.js to separate it from the single node.js thread, then that is usually done with the child process module that is part of node.js. You create a new process (which can be a node.js process or some other program running in the process) and you can then communicate with that other process.
If the controller function exports.sendMail is running, when the route
is called again, "kill this process", and start again
exports.sendMail
This is something that would need to be an explicit feature of the nodemailer module in order for you to cancel an operation in process. How "in process" asynchronous operations are implemented and controlled is not a generic node.js thing, but is specific to how that specific module implements things and keeps track of things.
Looking into the code for the node-mailer and more specifically the smtp-connection module, it looks like it uses plain async node.js socket code. That means it does not create any new threads or processes on its own.
As for your setInterval() calls, you need to make sure that any body of code that creates a setInterval() keeps track of the interval timer ID and eventually clears the interval so it stops and you don't keep piling up more and more interval timers. Another possibility is that you have only one interval and it does checking for all outstanding operations (rather than have a separate interval for each one).
From a quick look, I think you don't really need to put the sendMail function inside postlinevalidation. If you want to control it, you could run it in a different script from the express app. You can use something like pm2 or parallelshell to run multiple scripts at the same time.
If you are using setInterval then you can use clearInterval to stop the setInterval based on your condition. Whenever you call a setInterval function, it returns an id using which you can stop the setInterval.
var interval = setInterval(doStuff, 5000);
function doStuff() {
if(your_condition) {
clearInterval(interval);
}
}
Consider the following setup, regarding asynchronous functions:
Client.prototype.auth = function(callback) {
//authenticate the client
//run callback
};
Client.prototype.get = function() {
this.auth(function(){
//run the rest of this `get` function
}
};
The get function is called numerous times through an eventlistener, and this event fires only once
The very first get should start the authentication which stays valid for every subsequent calls
The authentication function is takes a couple of seconds to complete
Every subsequent get call does not need to reauthenticate because it is still valid because of the first function call
Every subsequent get call should only be run after the client is authenticated. If it is not authenticated it should wait for the authentication to finish
The point is to prevent 10 get calls to fire 10 auth calls. Whenever the 1st auth function gets called, the other 9 get calls should wait for it to finish and then carry on with the rest of the get function (while being authenticated)
I cant get my head around this. I tried to keep this example as simple as possible
I think solution for you is caching. Make a cache that will hold value isUserAutheniticated and isAuthenitcationProcess and when you need to call auth just check if user is authenticated and if not call it. Inside auth subscribe callback, check if authentication process is open if not do authentication set and call all registered callbacks. Globallist is not cleanest option to implement Observable pattern so you can do it in other way
Here is my idea:
var isAuthenticated = false;
var isAuthenticatioProcess = false;
var authenticationCallbacks = [];
Client.prototype.auth = function(callback) {
authenitcationCallbacks.push(callback);
if (isAuthenticonProcess) {
return;
}
//authenticate
authenitcationCallbacks.forEach(function(call) {
call();
});
authenitcationCallbacks = [];
isAuthenticonProcess = false;
isAuthenticated = true;
};
Client.prototype.get = function() {
if (isAuthenticated) {
this.auth(function(){
//run the rest of this `get` function
}
} else {
function(){
//run the rest of this `get` function
}
}
};
If you could use Async.js look at this answer