How to "interact" with another instance of a function?

#1

(Although what I'm trying to achieve can be done with core nodes, the reason I'd like to do this is to further my understanding of Javascript and Node-RED in general.)

I have written this little sleep dimmer function (link to other forum post), but I'd like to implement some kind of msg.reset to cancel the sleep dimmer.

Is there a way I can interact with the original "thread" or instance, to stop it, when the function itself receives a value in msg.reset?

(If you can correct any of my assumptions or wrong ideas about this, I'd be grateful too)

If not, is there a better way to achieve this?

#2

Assuming this msg.reset is being passed into the same node... then yes - you need to save the instance of the timeout context.foo = setTimeout(.... then it will be available so you can call clearTimeout(context.foo).

Of course this will mean you can only have one running at a time... so you may need to think how to handle that if another msg arrives to trigger another timeout... do you ignore it ? or cancel existing and restart ? or... up to you.

#3

Thanks @dceejay. I'll go with one running at a time - I'd like to use the node status and I guess it would be impossible to show the status of multiple instances!

Will give that a go.

#4

Well yes. If it’s controllong one set of lights would be tricky to have two rodents running. So you need to block that from occurring.

#5

Just trying to get my head around this now.

Do you mean I should save a value into context to indicate that a given fadeout is running? Then look it up and clear the timeout? Or are you talking about storing the function itself in context? Can you give me a pointer as to how I might do that?

I've just read up on storing a function in a variable, but to be honest I don't fully understand.

#6

the instance of the timeout. Exactly as I wrote it

#7

I think I've not understood, because this is now happening:

Here's the code I changed it to:

if (msg.reset == "yep") {
    clearTimeout(context.foo)
}
    
setTimeout(function(){
    var i = startval;
    function f() {
        if (i>0) {
            i--;
            msg.payload = i;
            node.send(msg);
            var fadetxt = "Fading " + (i*steptime/1000).toString() + "s, " + i.toString() + "/" + startval.toString();
            node.status({fill:"green", shape:"ring", text:fadetxt});
        } else {
            node.status({});
            return;
        }
        context.foo = setTimeout( f, steptime );
    }
    f();
},initialdelay)
#9

You need to test whether msg.reset exists first, also consider whether you want to go on and restart the timer in the case of the reset message.

#10

I thought we should use context.set() and context.get()

Something like this:

var id = setTimeout(...)
context.set('id', id);

and

var id = context.get('id');
clearTimeout(id);
#11

well yes that is "more correct" and would allow you to persist the object... but in this case I don't think persistence would be required so I think it's simper to understand.

#12

Alright!
One other benefit using context.set(): it is displayed in the menu Context Data.

#13

I don't think I have a problem with the logic of testing for msg.reset, and knowing how to deal with different scenarios (e.g. discard, reset timer and start counting, etc.) but I am really struggling with how to clear the timeout.

I understand that I need to save the function into memory. I have chosen to save to global whilst I figure this out (just for my own testing and understanding, as this means I can use context explorer to see it, and also access it from another node to e.g. test clearing the timeout from another function).

  • My main function has a function within it, in order to be able to have 2 delays (initial delay, and repeating delay for each fading step).
  • I have set the outer function (the initial delay) to memory.
  • When I start the timer, I can use context explorer and I see "[Object Object]"

However when I load this back into memory from another function node, then clearTimeout, the fader just keeps going.

To make it clearer, here's my code (yes, I know I used "id", just ignore the name for now):

FADER FUNCTION

var id = setTimeout(function(){
    var i = startval;
    function f() {
        if (i>0) {
            i--;
            msg.payload = i;
            node.send(msg);
            var fadetxt = "Fading " + (i*steptime/1000).toString() + "s, " + i.toString() + "/" + startval.toString();
            node.status({fill:"green", shape:"ring", text:fadetxt});
        } else {
            node.status({});
            return;
        }
        setTimeout( f, steptime );
    }
    f();
},initialdelay)
global.set('id',id);

And then...

TIMER CLEAR FUNCTION (different node, which I can inject message to for testing)

var id = global.get('id');
clearTimeout(id);

When I inject a message into this second function, it carries on counting

#14

You need to store the second setTimeout id:

var id = setTimeout(function(){
    var i = startval;
    function f() {
        if (i>0) {
            i--;
            msg.payload = i;
            node.send(msg);
            var fadetxt = "Fading " + (i*steptime/1000).toString() + "s, " + i.toString() + "/" + startval.toString();
            node.status({fill:"green", shape:"ring", text:fadetxt});
        } else {
            node.status({});
            return;
        }
        id = setTimeout( f, steptime );
        global.set('id',id);
    }
    f();
},initialdelay)
global.set('id',id);

BTW the setTimeout returns an ID:

The clearTimeout() method clears a timer set with the setTimeout() method.

The ID value returned by setTimeout() is used as the parameter for the clearTimeout() method.

See https://www.w3schools.com/jsref/met_win_cleartimeout.asp

1 Like
#15

don't use global. It would mean you can only have one instance of your node (as they would all try to use the same global variable)... use a local context one instead.