const timer = setTimeout(({a, b}) => {
console.log(a + b)
}, 3000, {a:1, b:2});
setTimeout(() => {
Object.assign(timer._timerArgs,[{a:2, b:2}])
}, 1000)
// Output: 4
Please have a look at this. What I'm going to do is, going to change the timer args if needed before it's called.
I don't want to use clearTimeout and setTimeout again for this process.
But not sure this is the right way. And plus how can I set the priority per each timer in case the timeout will be the same.
I don't know where you got ._timerArgs from. I've never seen that. Without dipping into undocumented properties (that are only present in node.js), you can do it like this:
const objA = {a:1, b:2};
const timer = setTimeout(({a, b}) => {
console.log(a + b);
}, 500, objA);
objA.a = 2;
objA.b = 3;
This will output 5 which reflects that you changed the property values before the timer callback fired.
Since objects in Javascript are passed by pointer (not copied), you can still modify the object that objA points at any time before the timer fires and see the effect inside the timer callback.
But, then you don't even have to pass the arguments into the setTimeout(). You can just reference a parent scoped variable in the callback:
const objA = {a:1, b:2};
const timer = setTimeout(() => {
console.log(objA.a + objA.b);
}, 500);
objA.a = 2;
objA.b = 3;
This will also output 5.
If you want arbitrary argument modification (not properties embedded in an object), and you want it to be only using supported, standard tools that work in all implementations of Javascript, then you can't do that with just setTimeout(). You could make your own timer wrapper though:
class MyTimer {
constructor(fn, t, ...args) {
this.args = args;
this.fired = false;
this.timer = setTimeout(() => {
this.fired = true;
fn.apply(null, this.args);
}, t);
}
cancel() {
clearTimeout(this.timer);
}
hasFired() {
return this.fired;
}
}
const timer = new MyTimer((...args) => {
console.log("timer callback arguments:", args);
}, 500, "hello", "goodbye");
timer.args = ["ola", "adios", "amor", "amigo"];
You can then modify the array in the .args property at any time before the timer fires and they will be passed to the timer callback.
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 have the following code :
let bindings =
{
on : (messageName,callback) =>
{
bindings[messageName] = callback
}
}
bindings.on('test',(params) =>
{
setTimeout( () =>
{
console.log("call id " , params.callId)
},~~(Math.random()*100))
})
let data = {callId : 1 }
for (let i=0;i<5;i++)
{
bindings['test'](data)
data.callId++
}
it produces the output
call id 6
call id 6
call id 6
call id 6
call id 6
call id 6
I know this issue can be solved with a bind https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/Function/bind , but I cannot find the correct way to implement this and keep the actual design
adding a const fix the issue but I would like to find a more elegant/generic way to fix the issue
bindings.on('test',(params) =>
{
const callId = params.callId
setTimeout( () =>
{
console.log("call id " , callId)
},~~(Math.random()*100))
})
It is less about the implementation of your event logic but on the invocation of it using a reference that is being updated by a loop.
Since the implementation is an async execution due to the internal setTimeout, the invocation loop executes completely before the first event logic is executed. So because the input, params is a reference to the object data, when the loop is completed the callId value is now 6 which when referenced by any of the async calls will be that value.
Essentially the for loop is queuing up 5 async operations where each one is using the same referenced object, data. The loop is also updating a property value of the data object for each iteration. Because of how JavaScript handles asynchronous operations, the loop will complete before any of the asynchronous operations begin because it is still following the initial synchronous execution of the script.
One way to get around this is to create a new input object for each loop iteration:
let bindings = {
on: (messageName, callback) => {
bindings[messageName] = callback
}
}
bindings.on('test', (params) => {
setTimeout(() => {
console.log("call id ", params.callId)
}, ~~(Math.random() * 100));
});
for (let i = 0; i < 5; i++) {
bindings['test']({callId: i + 1});
}
I have a function which I need to call a number of time and instead of using a for loop I'm using async.whilst. But what I need is that the next call to function is not made before the previous call completes, which is not what's happening with async.whilst. Is there a way to implement this (I'm using setTimeout to pause between each call but it is not very clean).
Many thanks, C
i'd use the forever construct. Assuming your function's name is myFunction and accepts as parameter a callback:
var count = 0;
var limit = 10; // set as number of the execution of the function
async.forever(
function(next) {
myFunction(function () {
count++;
if(count < limit) {
next();
} else {
next(true);
}
})
},
function(ended) {
// function calling iteration ended
}
);
Is there a way to make Node.js stream as coroutine.
Example
a Fibonacci numbers stream.
fibonacci.on('data', cb);
//The callback (cb) is like
function cb(data)
{
//something done with data here ...
}
Expectation
function* fibonacciGenerator()
{
fibonacci.on('data', cb);
//Don't know what has to be done further...
};
var fibGen = fibonacciGenerator();
fibGen.next().value(cb);
fibGen.next().value(cb);
fibGen.next().value(cb);
.
.
.
Take desired numbers from the generator. Here Fibonacci number series is just an example, in reality the stream could be of anything a file, mongodb query result, etc.
Maybe something like this
Make the 'stream.on' function as a generator.
Place yield inside the callback function.
Obtain generator object.
Call next and take the next value in stream.
Is it at-least possible if yes how and if not why? Maybe a dumb question :)
If you don't want to use a transpiler (e.g. Babel) or wait until async/await make it to Node.js, you can implement it yourself using generators and promises.
The downside is that your code must live inside a generator.
First, you can make a helper that receives a stream and returns a function that, when called, returns a promise for the next "event" of the stream (e.g. data).
function streamToPromises(stream) {
return function() {
if (stream.isPaused()) {
stream.resume();
}
return new Promise(function(resolve) {
stream.once('data', function() {
resolve.apply(stream, arguments);
stream.pause();
});
});
}
}
It pauses the stream when you're not using it, and resumes it when you ask it the next value.
Next, you have a helper that receives a generator as an argument, and every time it yields a promise, it resolves it and passes its result back to the generator.
function run(fn) {
var gen = fn();
var promise = gen.next().value;
var tick = function() {
promise.then(function() {
promise = gen.next.apply(gen, arguments).value;
}).catch(function(err) {
// TODO: Handle error.
}).then(function() {
tick();
});
}
tick();
}
Finally, you would do your own logic inside a generator, and run it with the run helper, like this:
run(function*() {
var nextFib = streamToPromises(fibonacci);
var n;
n = yield nextFib();
console.log(n);
n = yield nextFib();
console.log(n);
});
Your own generator will yield promises, pausing its execution and passing the control to the run function.
The run function will resolve the promise and pass its value back to your own generator.
That's the gist of it. You'd need to modify streamToPromises to check for other events as well (e.g. end or error).
class FibonacciGeneratorReader extends Readable {
_isDone = false;
_fibCount = null;
_gen = function *() {
let prev = 0, curr = 1, count = 1;
while (this._fibCount === -1 || count++ < this._fibCount) {
yield curr;
[prev, curr] = [curr, prev + curr];
}
return curr;
}.bind(this)();
constructor(fibCount) {
super({
objectMode: true,
read: size => {
if (this._isDone) {
this.push(null);
} else {
let fib = this._gen.next();
this._isDone = fib.done;
this.push(fib.value.toString() + '\n');
}
}
});
this._fibCount = fibCount || -1;
}
}
new FibonacciGeneratorReader(10).pipe(process.stdout);
Output should be:
1
1
2
3
5
8
13
21
34
55
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");