I am learning node js and need some clarification.
var EventEmitter = require('events').EventEmitter;
var getResource = function(c) {
var emitter = new EventEmitter();
process.nextTick(function() {
var count = 0;
emitter.emit("start")
var t = setInterval(function() {
c
emitter.emit("data", ++count);
if (count === c) {
emitter.emit("end");
//clearInterval(t);
}
}, 1000)
});
return emitter;
}
var r = getResource(5);
r.on("start", function() {
console.log("I have started");
})
r.on("data", function(d) {
console.log("Received " + d);
})
r.on("end", function() {
console.log("I have ended");
})
If I uncomment the setInterval part it behaves as expected ie., prints the data until 5 and end event is emitted.
If I comment it, then data is printed until 5 and end event is emitted. After that it runs in indefinite loop and prints continuously. Why is my code
if(count === c){...}
does not check and ends the loop ?
The emit method is a generic one. It just emits an event & optionally passes data.
emitter.emit(eventName, data);
where eventName is a string & data is an arbitrary value.
It is up to the programmer to make use of that as per the requirement.
Passing end as event name means nothing to emit method, as for the method, it is just another call. In your case, clearInterval is needed to actually end data events. Only to the receiver of the events, end event makes sense.
So, if you emit end event, the receiver will know there would be no more data event.
Consider emitting end event as a signal to receiver. But, you have to actually make sure there are no more events(data) after end event.
until clearInterval is called, setInterval will execute the code
in recurring time intervals.
Please refer the NodeJs doc on Timers.
Related
I am having newbie difficulties implementing clearInterval with a certain payload.
The
if(msg.payload.state === "OFF")
works, but the timer doesn't stop.
Amongst many resources, I have seen how to stop setInterval in JS in node-red..?, node.js: how to use setInterval and clearInterval?, clearInterval doesn't clearInterval & clearInterval On Image Slider Plugin
I did have a similar function, "dominos" that I used to make sure I was parsing the "OFF" payload.
I have included a lot of commented code to show what I had tried. Sorry about the mess!
Is it an issue with my syntax?
Is the code logical?
var input = msg.payload.state;
let timer = 0;
var red = {"state":"ON","brightness":255,"color":{"r":255,"g":0,"b":0}};
var green = {"state":"ON","brightness":255,"color":{"r":0,"g":255,"b":0}};
function xmas() { // REPEATS!
node.send({payload:red});
setTimeout(function(){
node.send({payload:green});
}, 1500);
}
// repeat with 3 second interval
timer = setInterval(() => xmas(), 3000);
if (msg.payload.state === "OFF") {
timer = clearInterval();
}
The important thing to remember about a function node is that it's state is totally reset for each incoming message. This means that timer will ALWAYS be reset to zero by the let timer = 0;
If you want to store any state between messages then you need to use the context.
Also the way you are using clearInterval() will never work, you need to pass the reference to the timer object to this function for it to do anything useful.
The following function node should do what I think you intended.
var red = {"state":"ON","brightness":255,"color":{"r":255,"g":0,"b":0}};
var green = {"state":"ON","brightness":255,"color":{"r":0,"g":255,"b":0}};
if (msg.payload.state != "OFF") {
var timer = setInteval(()=>{
node.send({payload: red});
setTimeout(() => {
node.send({payload: green});
}, 1500);
}, 3000);
context.set("timer",timer);
} else {
var timer = context.get("timer");
clearInterval(timer);
}
So I'm working with websockets to process data from website's API. For every new event I also send some http requests back to the website in order to obtain more data. Up untill now everything has worked fine, but now that I started using async requests to speed it up a bit things got a bit different. My code used to process one event and then move on to the next one (these events come in extremely quick - around 10 per second) but now it just seems to ignore the async (non blocking) part and move on to the next event and that way it just skips over half of the code. Note that the code works fine outside the Pusher. I'm using the 'pusher-client' module. My code looks like this:
var Request = require("request");
var requestSync = require('sync-request');
var Pusher = require('pusher-client');
var events_channel = pusher.subscribe('inventory_changes');
events_channel1.bind('listed', function(data)
{
var var2;
//Async request (to speed up the code)
function myFunction(callback){
request("url", function(error, response, body) {
if (!error && response.statusCode == 200)
{
result = JSON.stringify(JSON.parse(body));
return callback(null, result);
}
else
{
return callback(error, null);
}
});
}
myFunction(function(err, data){
if(!err)
{
var2 = data
return(data);
}
else
{
return(err);
}
});
//The part of the code below waits for the callback and the executes some code
var var1 = var2;
check();
function check()
{
if(var2 === var1)
{
setTimeout(check, 10);
return;
}
var1 = var2;
//A CHUNK OF CODE EXECUTES HERE (connected to the data from the callback)
}
});
In conclusion the code works, but not inside the pusher due to the pusher skipping the asynchronous request. How would I make the pusher wait for my async request to finish, before processing the next event (I have no idea)? If you happen to know, please let me know :)
You need to implement a queue to handle events one after another. I'm curious how it worked before, even without Pusher you'd have to implement some queue mechanism for it.
const eventsQueue = []
events_channel1.bind('listed', function(data) {
eventsQueue.push(data)
handleNewEvent()
})
let processingEvent = false
function handleNewEvent() {
if (processingEvent) return // do nothing if already processing an event
processingEvent = true
const eventData = eventsQueue.shift() // pick the first element from array
if (!eventData) return // all events are handled at the moment
... // handle event data here
processingEvent = false
handleNewEvent() // handle next event
}
Also, you should call clearTimeout method to clear your timeout when you don;t need it anymore.
And it's better to use promises or async/await instead of callbacks. Your code will be much easier to read and maintain.
I'm developing an app with the following node.js stack: Express/Socket.IO + React. In React I have DataTables, wherein you can search and with every keystroke the data gets dynamically updated! :)
I use Socket.IO for data-fetching, so on every keystroke the client socket emits some parameters and the server calls then the callback to return data. This works like a charm, but it is not garanteed that the returned data comes back in the same order as the client sent it.
To simulate: So when I type in 'a', the server responds with this same 'a' and so for every character.
I found the async module for node.js and tried to use the queue to return tasks in the same order it received it. For simplicity I delayed the second incoming task with setTimeout to simulate a slow performing database-query:
Declaration:
const async = require('async');
var queue = async.queue(function(task, callback) {
if(task.count == 1) {
setTimeout(function() {
callback();
}, 3000);
} else {
callback();
}
}, 10);
Usage:
socket.on('result', function(data, fn) {
var filter = data.filter;
if(filter.length === 1) { // TEST SYNCHRONOUSLY
queue.push({name: filter, count: 1}, function(err) {
fn(filter);
// console.log('finished processing slow');
});
} else {
// add some items to the queue
queue.push({name: filter, count: filter.length}, function(err) {
fn(data.filter);
// console.log('finished processing fast');
});
}
});
But the way I receive it in the client console, when I search for abc is as follows:
ab -> abc -> a(after 3 sec)
I want it to return it like this: a(after 3sec) -> ab -> abc
My thought is that the queue runs the setTimeout and then goes further and eventually the setTimeout gets fired somewhere on the event loop later on. This resulting in returning later search filters earlier then the slow performing one.
How can i solve this problem?
First a few comments, which might help clear up your understanding of async calls:
Using "timeout" to try and align async calls is a bad idea, that is not the idea about async calls. You will never know how long an async call will take, so you can never set the appropriate timeout.
I believe you are misunderstanding the usage of queue from async library you described. The documentation for the queue can be found here.
Copy pasting the documentation in here, in-case things are changed or down:
Creates a queue object with the specified concurrency. Tasks added to the queue are processed in parallel (up to the concurrency limit). If all workers are in progress, the task is queued until one becomes available. Once a worker completes a task, that task's callback is called.
The above means that the queue can simply be used to priorities the async task a given worker can perform. The different async tasks can still be finished at different times.
Potential solutions
There are a few solutions to your problem, depending on your requirements.
You can only send one async call at a time and wait for the first one to finish before sending the next one
You store the results and only display the results to the user when all calls have finished
You disregard all calls except for the latest async call
In your case I would pick solution 3 as your are searching for something. Why would you use care about the results for "a" if they are already searching for "abc" before they get the response for "a"?
This can be done by giving each request a timestamp and then sort based on the timestamp taking the latest.
SOLUTION:
Server:
exports = module.exports = function(io){
io.sockets.on('connection', function (socket) {
socket.on('result', function(data, fn) {
var filter = data.filter;
var counter = data.counter;
if(filter.length === 1 || filter.length === 5) { // TEST SYNCHRONOUSLY
setTimeout(function() {
fn({ filter: filter, counter: counter}); // return to client
}, 3000);
} else {
fn({ filter: filter, counter: counter}); // return to client
}
});
});
}
Client:
export class FilterableDataTable extends Component {
constructor(props) {
super();
this.state = {
endpoint: "http://localhost:3001",
filters: {},
counter: 0
};
this.onLazyLoad = this.onLazyLoad.bind(this);
}
onLazyLoad(event) {
var offset = event.first;
if(offset === null) {
offset = 0;
}
var filter = ''; // filter is the search character
if(event.filters.result2 != undefined) {
filter = event.filters.result2.value;
}
var returnedData = null;
this.state.counter++;
this.socket.emit('result', {
offset: offset,
limit: 20,
filter: filter,
counter: this.state.counter
}, function(data) {
returnedData = data;
console.log(returnedData);
if(returnedData.counter === this.state.counter) {
console.log('DATA: ' + JSON.stringify(returnedData));
}
}
This however does send unneeded data to the client, which in return ignores it. Somebody any idea's for further optimizing this kind of communication? For example a method to keep old data at the server and only send the latest?
I'm trying to implement and existing solution in node.js, specifically, using express.js framework. Now, the existing solution works as follows:
server exposes a GET service that clients can connect to
when a client calls the GET service, the client number increments (a global variable) and then the number of clients is checked;
if there are not at least 3 clients connected, the service is in endless loop, waiting for other clients to connect
if (or rather, when) the rest of the two clients connect, the service sends respond to everyone that enough clients are connected (a 'true' value).
So what basically happens is, the client connects and the connection is active (in a loop) until enough clients connect, then and only then there is a response (to all clients at the same time).
Now I'm not expert in these architectures, but from what I think, this is not a correct or good solution. My initial thought was: this must be solved with sockets. However, since the existing solution works like that (it's not written in node.js), I tried to emulate such behaviour:
var number = (function(){
var count = 0;
return {
increase: function() {
count++;
},
get: function(){
return count;
}
};
})();
app.get('/test', function(req, res){
number.increase();
while (number.get() < 3) {
//hold it here, until enough clients connect
}
res.json(number.get());
});
Now while I think that this is not a correct solution, I have a couple of questions:
Is there any alternative to solving this issue, besides using sockets?
Why does this "logic" work in C#, but not in express.js? The code above hangs, no other request is processed.
I know node.js is single-threaded, but what if we have a more conventional service that responds immediately, and there are 20 requests all at the same time?
I would probably use an event emitter for this:
var EventEmitter = require('events').EventEmitter;
var emitter = new EventEmitter();
app.get('/', function(req, res) {
// Increase the number
number.increase();
// Get the current value
var current = number.get();
// If it's less than 3, wait for the event emitter to trigger.
if (current < 3) {
return emitter.once('got3', function() {
return res.json(number.get());
});
}
// If it's exactly 3, emit the event so we wake up other listeners.
if (current === 3) {
emitter.emit('got3');
}
// Fall through.
return res.json(current);
});
I would like to stress that #Plato is correct in stating that browsers may timeout when a response takes too much time to complete.
EDIT: as an aside, some explanation on the return emitter.once(...).
The code above can be rewritten like so:
if (current < 3) {
emitter.once('got3', function() {
res.json(number.get());
});
} else if (current === 3) {
emitter.emit('got3');
res.json(number.get());
} else {
res.json(number.get());
}
But instead of using those if/else statements, I return from the request handler after creating the event listener. Since request handlers are asynchronous, their return value is discarded, so you can return anything (or nothing). As an alternative, I could also have used this:
if (current < 3) {
emitter.once(...);
return;
}
if (current === 3) {
...etc...
Also, even though you return from the request handler function, the event listener is still referencing the res variable, so the request handler scope is maintained by Node until res.json() in the event listener callback is called.
Your http approach should work
You are blocking the event loop so node refuses to do any other work while it is in the while loop
You're really close, you just need to check every now and then instead of constantly. I do this below with process.nextTick() but setTimeout() would also work:
var number = (function(){
var count = 0;
return {
increase: function() {
count++;
},
get: function(){
return count;
}
};
})();
function waitFor3(callback){
var n = number.get();
if(n < 3){
setImmediate(function(){
waitFor3(callback)
})
} else {
callback(n)
}
}
function bump(){
number.increase();
console.log('waiting');
waitFor3(function(){
console.log('done');
})
}
setInterval(bump, 2000);
/*
app.get('/test', function(req, res){
number.increase();
waitFor3(function(){
res.json(number.get());
})
});
*/
I have an event on server that is triggered when the client sends a message:
socket.on('message', function(data){
message.push(data);
});
And now when all clients send a message, I want to wait for 3 seconds and then call a function.
I tried with Interval:
var index=0;
var timer;
socket.on('message', function(data){
message.push(data);
if (index==0) //only once
timer = setInterval(call(), 3000);
index++;
});
function call(){
io.sockets.emit("confirmation", {status: "All messages are delivered"});
clearInterval(timer); // Clear interval
}
The problem is that the function call() call immediately and not after 3 seconds.
The second problem is that clearInterval(timer) in function call, does not work because the function call repeats.
If you only want this to occur once, you should use setTimeout instead of setInterval.
This will save you from needing to clear the interval later, and makes the code more explicit that you want it to happen exactly once, 3 seconds from "now."
In addition "(...)" at the end of a function name causes it to be called immediately. You want to pass the function "call" as a parameter, not cause it to be called immediately.
I would also recommend you name the function something other than call, as this is already a name of a standard function in Javascript, and therefore can get confusing quickly. Even calling it callback would reduce confusion.
So, what you're looking for is something like this:
setTimeout(callback,3000);
in place of this
setInterval(call(), 3000);
You are making a call to the function from setInterval function.
It should betimer = setInterval(call, 3000); and not timer = setInterval(call(), 3000);
Thanks for all the answers. I solve the problem.
The problem was that I wanted to create a setInterval() within io.on("connection", function(socket){}, but it makes for each client separately. I did not want this.
Now I put before and it works:
var index=0;
var timer;
var check=false;
timer = setInterval(function(){fcall()}, 3000);
function fcall(){
if(check)
{
io.sockets.emit("confirmation", {status: "All messages are delivered"});
clearInterval(timer); // Clear interval
timer=0;
}
}
io.on("connection", function(socket){
socket.on('message', function(data){
message.push(data);
if (index==0) //only once
check = true;
index++;
});
}