I've implemented a native function which takes a callback. NodeJS knows the interface, but it doesn't know anything about its implementation. This native function receives a callback and will call it when the result is ready. I don't want the event loop to exit while the callback hasn't been called.
Here is an example of such a problem.
Currently I need to do some I/O (even if it's a dumb timeout) to force NodeJS to wait for my function.
In Boost.Asio I'd just instantiate a work object and destroy it when the callback is called. Boost.Asio's event loop wouldn't exit while this object is kept around. Is there a similar approach for NodeJS? What do I use in NodeJS (bonus if your answer doesn't mention timers)?
The best way to do that would be writing a C++ addon and using one of the handles offered by libuv (of course, the one that fits your requirements - see the official documentation for further details).
If you don't want to do that or if you can't do that (that is the case, if I've correctly understood the question), a viable solution not mentioned in the other answers is using process.nextTick to schedule a function that checks at each tick if the loop can expires or not.
see here for further details about process.nextTick.
As a minimal, working, never-ending example:
var process = require('process')
var stop = false;
var f = function() { if(!stop) process.nextTick(f) }
f()
This way your function is in charge of setting the stop control variable once it finishes to execute, then the loop will stop.
If you have multiple callbacks to wait for, simply use a counter and check it looking for 0.
If you don't want to explicitly set and update the value of the counter each time you add a new function (error-prone), you can easily write a launcher to start your functions that increments the counter and schedules a check on the next tick if needed.
You could also pass a callback as an extra argument to your functions to notify when they are over, so that they have not to deal explicitly with the counter itself.
A plus of using a dedicated function that is scheduled on the next tick is that it's clear to the reader what you are doing.
On the other side, a fake server, a timeout scheduled in the future or an I/O stream resumed and never used are quite obscure, for the reader doesn't know immediately why you are doing that.
Method 1: Create a dummy server(updated for multiple callback):
var counter = 0;
var server = require('net').createServer().listen(); // <-- Dummy server
console.log('Dummy server start.');
var ffi = require('ffi'),
ref = require('ref')
var lib = ffi.Library('./libffi_async_demo', {
'print_thread_id': [ 'void', [] ],
'run_delayed': [ 'void', [ 'pointer' ] ],
});
var checkExit = function (){
counter--;
if (counter===0) {
server.close(); // <-- exit Dummy Server
console.log('Dummy server stop.');
}
}
// Wrapper for lib.run_delay()
run_delay = function(cb) {
counter++; // <-- increase counter
lib.run_delayed(cb);
}
var callback1 = ffi.Callback('void', [], function() {
console.log("js callback1 started");
lib.print_thread_id();
console.log("js callback1 finished");
checkExit(); // <-- call at the end of each callback
})
var callback2 = ffi.Callback('void', [], function() {
console.log("js callback2 started");
lib.print_thread_id();
console.log("js callback2 finished");
checkExit(); // <-- call at the end of each callback
})
var callback3 = ffi.Callback('void', [], function() {
console.log("js callback3 started");
lib.print_thread_id();
console.log("js callback3 finished");
checkExit(); // <-- call at the end of each callback
})
run_delay(callback1); // use wrapper
run_delay(callback2); // use wrapper
run_delay(callback3); // use wrapper
Method 2: Long timeout, callback end process
var timeout; // Hold timeout reference from setTimeout()
var ffi = require('ffi'),
ref = require('ref')
var lib = ffi.Library('./libffi_async_demo', {
'print_thread_id': [ 'void', [] ],
'run_delayed': [ 'void', [ 'pointer' ] ],
});
var callback = ffi.Callback('void', [], function() {
console.log("js callback started");
lib.print_thread_id()
console.log("js callback finished");
// Use one of the following 3:
//timeout.unref(); // <-- remove timer from Node event loop
//require('process').exit(); //<-- end process
clearTimeout(timeout); // <-- cancel timer
})
lib.run_delayed(callback)
timeout = setTimeout(function() { }, 3600000); // <-- reasonably long timeout, eg. 1hr
You can also use the stdin Readable stream to hold on exiting the loop.
const callback = ffi.Callback('void', [], function() {
// do your stuff here
// switch stream out of flowing mode.
process.stdin.pause();
});
// set stream into flowing mode ("old mode")
process.stdin.resume();
lib.run_delayed(callback);
Reference: the Note in https://nodejs.org/api/process.html#process_process_stdin
Start repl somewhere in your code, it will prevent your app from exit.
const repl = require('repl');
repl.start('> ')
When you are done just call
process.exit()
https://nodejs.org/api/repl.html
create big timeout - it'll prevent node from exiting and also guards you from waiting indefinitely for (external to node) result:
var ffi = require('ffi'),
ref = require('ref')
var ffiTimeout;
var lib = ffi.Library('./libffi_async_demo', {
'print_thread_id': [ 'void', [] ],
'run_delayed': [ 'void', [ 'pointer' ] ],
});
var ffiDidTimedOut = false;
var cancelFfi = function(timeoutCb) {
return function() {
ffiDidTimedOut = true;
timeoutCb();
}
}
var callback = ffi.Callback('void', [], function() {
if (ffiDidTimedOut) {
return; // sorry, we waited too long and already started doing something else
}
// all good, async ffi finished within expected time and we are back in our js land
clearTimeout(ffiTimeout);
lib.print_thread_id()
})
lib.run_delayed(callback)
// continueIfCancelledCallback is your continuation "what to do id ffi actually takes more than 20 seconds to run"
ffiTimeout = setTimeout(cancelFfi(continueIfCancelledCallback), 20000); // 20 seconds
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);
}
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 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.
I'm building my first node.js application on my Raspberry Pi which I am using to control an air conditioner via LIRC. The following code is called when you want to increase the temperature of the AC unit. It sends a LIRC command every 250 milliseconds depending on how many degrees you want to increase it by. This code works as expected.
var iDegrees = 5;
var i = 0;
var delay = 250 // The delay in milliseconds
function increaseTemperatureLoop(){
i++;
//lirc_node.irsend.send_once("ac", "INCREASE", function() {});
console.log(i);
// Call the fucntion/loop again after the delay if we still need to increase the temperature
if (i <= iDegrees){
timer = setTimeout(increaseTemperatureLoop, delay);
}
else {
res.json({"message": "Success"});
}
}
// Start the timer to call the recursive function for the first time
var timer = setTimeout(increaseTemperatureLoop, delay);
I'm having a hard time working with the asynchronous nature of node.js. Once my recursive function is done, I return my json to the browser as shown in the code above. By habit, I feel like I should return the json in a line of code after my initial function call like below but obviously that wouldn't wait for all of the LIRC calls to be successful - it seems silly to have it inside of the function:
var timer = setTimeout(increaseTemperatureLoop, delay);
res.json({"message": "Success"});
What if I have a bunch of other stuff to do after my LIRC sends are done but before I want to send my json back to the browser? Or what if that block of code throws an error...
My second question is, how do I properly wrap the LIRC call in a try/catch and then if there is an error, stop the recursive calls, pass the error back up, and then pass this back to the browser along with the actual error message:
res.json({"message": "Failed"});
For track end of the cycle execution task, you can use a callback.
In order to know whether completed all routine tasks, you can use the task queue.
Monitor and report bugs to the top - it is possible with the help of
three of the same callback.
In general, it is desirable to wrap everything into a single object.
Some example for reflection:
var lircTasks = function __self (){
if (typeof __self.tasks === "undefined") __self.tasks = 0;
__self.func = {
increaseTemperature: function() {
// lirc_node.irsend.send_once("ac", "INCREASE_TEMPERATURE", function() {});
},
increaseFanPower: function() {
// lirc_node.irsend.send_once("ac", "INCREASE_FANPOWER", function() {});
}
}
var fab = function () {
__self.tasks++;
this.i = 0;
this.args = arguments[0];
this.callback = arguments[1];
this.run = function __ref(taskName) {
if (taskName) this.taskName = taskName;
if (this.i<this.args.deg) {
try {
__self.func[this.taskName]();
} catch(e) {
__self.tasks--;
this.callback( {message: "error", error: e, taskName: this.taskName, task: this.args, tasks: __self.tasks} );
}
this.i++;
setTimeout( __ref.bind(this), this.args.delay );
} else {
__self.tasks--;
this.callback({message:"complete", taskName: this.taskName, task: this.args, tasks: __self.tasks});
}
}
}
if ((arguments.length === 2) && (typeof arguments[1] === "function") && arguments[0].deg>0 && arguments[0].delay>=0) {
return new fab(arguments[0], arguments[1]);
}
}
function complete(e) {
console.log(e);
if (e.tasks === 0) console.log({message: "Success"});
}
lircTasks( {deg: 10, delay:100, device: "d1" }, complete ).run("increaseTemperature");
lircTasks( {deg: 20, delay:150, device: "d2" }, complete ).run("increaseTemperature");
lircTasks( {deg: 5, delay:100, device: "d3" }, complete ).run("increaseFanPower");
I want to stop of executing of my async.queue after first task error was occurred. I need to perform several similar actions in parallel with the concurrency restriction, but stop all the actions after first error. How can I do that or what should I use instead?
Assuming you fired 5 parallel functions, each will take 5 seconds. While in 3rd second, function 1 failed. Then how you can stop the execution of the rest?
It depends of what those functions do, you may poll using setInterval. However if your question is how to stop further tasks to be pushed to the queue. You may do this:
q.push(tasks, function (err) {
if (err && !called) {
//Will prevent async to push more tasks to the queue, however please note that
//whatever pushed to the queue, it will be processed anyway.
q.kill();
//This will not allow double calling for the final callback
called = true;
//This the main process callback, the final callback
main(err, results);
}
});
Here a full working example:
var async = require('async');
/*
This function is the actual work you are trying to do.
Please note for example if you are running child processes
here, by doing q.kill you will not stop the execution
of those processes, so you need actually to keep track the
spawned processed and then kill them when you call q.kill
in 'pushCb' function. In-case of just long running function,
you may poll using setInterval
*/
function worker(task, wcb) {
setTimeout(function workerTimeout() {
if (task === 11 || task === 12 || task === 3) {
return wcb('error in processing ' + task);
}
wcb(null, task + ' got processed');
}, Math.floor(Math.random() * 100));
}
/*
This function that will push the tasks to async.queue,
and then hand them to your worker function
*/
function process(tasks, concurrency, pcb) {
var results = [], called = false;
var q = async.queue(function qWorker(task, qcb) {
worker(task, function wcb(err, data) {
if (err) {
return qcb(err); //Here how we propagate error to qcb
}
results.push(data);
qcb();
});
}, concurrency);
/*
The trick is in this function, note that checking q.tasks.length
does not work q.kill introduced in async 0.7.0, it is just setting
the drain function to null and the tasks length to zero
*/
q.push(tasks, function qcb(err) {
if (err && !called) {
q.kill();
called = true;
pcb(err, results);
}
});
q.drain = function drainCb() {
pcb(null, results);
}
}
var tasks = [];
var concurrency = 10;
for (var i = 1; i <= 20; i += 1) {
tasks.push(i);
}
process(tasks, concurrency, function pcb(err, results) {
console.log(results);
if (err) {
return console.log(err);
}
console.log('done');
});
async documentation on github page is either outdated or incorrect, while inspecting the queue object returned by async.queue() method I do not see the method kill().
Nevertheless there is a way around it. Queue object has property tasks which is an array, simply assigning a reference to an empty array did the trick for me.
queue.push( someTasks, function ( err ) {
if ( err ) queue.tasks = [];
});