# setTimeout issue on a node with callback function

**URL:** <https://discourse.nodered.org/t/settimeout-issue-on-a-node-with-callback-function/5207>\
**Category:** General\
**Created:** [26 November 2018 13:04 UTC](https://discourse.nodered.org/t/settimeout-issue-on-a-node-with-callback-function/5207 "2018-11-26T13:04:29Z")\
**Posts on this page:** 10\
**Page:** 1

<div class="post-metadata">

**Author:** ![abajolle](https://avatars.discourse-cdn.com/v4/letter/a/df705f/32.png) [@abajolle](https://discourse.nodered.org/u/abajolle)\
**Post date:** [26 November 2018 13:04 UTC](https://discourse.nodered.org/t/settimeout-issue-on-a-node-with-callback-function/5207/1 "2018-11-26T13:04:29Z")

</div>

Hi dear Node-RED community.

I am connecting some digital inputs on my Siemens Simatic IoT2000 products, unfortunately none of the existing nodes are working well, so I have started to create my own. Now the behaviour is fine since I have added a callback function. But I still have an annoying bug. If I redeploy my flow then I have 2 readings of the pin, if I deploy a third time then I have 3 readings ... so to avoid that each time I deploy a modified flow with those nodes I have to restart my Node-RED.  
I am pretty sure it has to do with a poor clearance of the TimeOut, but I have not been able to figure it out so far 🙄.

Here is the js code of the node:

```auto
var sys = require('util')
var exec = require('child_process').exec;

module.exports = function(RED) 
{
  var mraa = require('mraa');
  function newSetInterval(callback, duration, callbackArguments)
	{
    callback.apply(this, callbackArguments);
    var args = arguments,
    scope=this;
    setTimeout(function() {newSetInterval.apply(scope, args);}, duration);
	}
  exec("echo 63 > /sys/class/gpio/export");
  function gpioDin(n) {
    RED.nodes.createNode(this,n);
    var node = this;
    node.pin = n.pin;
    node.updateInterval = n.updateInterval;
    node.mGpio = new mraa.Gpio(parseInt(node.pin));
    node.mGpio.mode(mraa.PIN_GPIO);
    node.mGpio.dir(mraa.DIR_IN);
    var lastPinState = node.mGpio.read();
    var currentPinState = 0;
    var timerId = newSetInterval(function()
		{
			if(node.pin != "ub")
			{			
				currentPinState = node.mGpio.read();
			}
			else
			{
				exec("cat /sys/class/gpio/gpio63/value", function(error, stdout, stderr) {
				currentPinState = parseInt(stdout)});
			}
			if(currentPinState === 0 || currentPinState === 1)
			{
				if (currentPinState != lastPinState)
				{
					if(currentPinState === 0)
						node.status({fill:"grey",shape:"ring",text:"Low"});
					else if(currentPinState === 1)
						node.status({fill:"grey",shape:"dot",text:"High"});
					var msg = { payload:currentPinState, topic:"D"+node.pin };
					node.send(msg);
				}
				lastPinState = currentPinState;
			}
		},parseInt(node.updateInterval));   
		
		this.on('close', function() {
			clearTimeout(timerId);
		});
	}	
  RED.nodes.registerType("IoT2000-gpio-din", gpioDin);
}

```

Any suggestions as to how do a proper reset of my Timeout when I redeploy ?

Cheers,

Antoine

---

<div class="post-metadata">

**Author:** ![TotallyInformation](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/totallyinformation/32/31_2.png) [@TotallyInformation](https://discourse.nodered.org/u/TotallyInformation)\
**Post date:** [26 November 2018 20:31 UTC](https://discourse.nodered.org/t/settimeout-issue-on-a-node-with-callback-function/5207/2 "2018-11-26T20:31:47Z")

</div>

You need to use the close event in order to remove any timeouts and close out anything that needs it.

It was a while back that I created it but my example library called jktesting may help you navigate the intricacies of a node's structure.

> **[TotallyInformation/node-red-contrib-jktesting](https://github.com/TotallyInformation/node-red-contrib-jktesting)**
>
> Test nodes for Node-RED for learning and experimenting, best practice, lots of hints and notes - TotallyInformation/node-red-contrib-jktesting

Specifically, you will need to do a `clearTimeout(timerId)` in the close section.

---

<div class="post-metadata">

**Author:** ![abajolle](https://avatars.discourse-cdn.com/v4/letter/a/df705f/32.png) [@abajolle](https://discourse.nodered.org/u/abajolle)\
**Post date:** [27 November 2018 00:22 UTC](https://discourse.nodered.org/t/settimeout-issue-on-a-node-with-callback-function/5207/3 "2018-11-27T00:22:10Z")

</div>

Hi Julian,

Thanks I will have a look at this, and hopefully get the insight I need to fix this bug.

Nevertheless I already have the clearTimeout in my node at the bottom, but it does not seem to have the correct behaviour.

> [@abajolle](#):
>
> ```auto
> this.on('close', function() { clearTimeout(timerId); });
> 
> ```

---

<div class="post-metadata">

**Author:** ![nlecaude](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/nlecaude/32/25_2.png) [@nlecaude](https://discourse.nodered.org/u/nlecaude)\
**Post date:** [27 November 2018 01:39 UTC](https://discourse.nodered.org/t/settimeout-issue-on-a-node-with-callback-function/5207/4 "2018-11-27T01:39:28Z")

</div>

You will need to return setInterval at the end of your newSetInterval function.

---

<div class="post-metadata">

**Author:** ![abajolle](https://avatars.discourse-cdn.com/v4/letter/a/df705f/32.png) [@abajolle](https://discourse.nodered.org/u/abajolle)\
**Post date:** [27 November 2018 02:05 UTC](https://discourse.nodered.org/t/settimeout-issue-on-a-node-with-callback-function/5207/5 "2018-11-27T02:05:50Z")

</div>

Hi Nathanael,

Simply like that or am I missing something ?

> [@abajolle](#):
>
> ```auto
> function newSetInterval(callback, duration, callbackArguments)
> { 
> callback.apply(this, callbackArguments);
> var args = arguments, scope=this;
> setTimeout(function() {newSetInterval.apply(scope, args);}, duration);
> return setInterval();
> }
> 
> ```

---

<div class="post-metadata">

**Author:** ![nlecaude](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/nlecaude/32/25_2.png) [@nlecaude](https://discourse.nodered.org/u/nlecaude)\
**Post date:** [27 November 2018 02:09 UTC](https://discourse.nodered.org/t/settimeout-issue-on-a-node-with-callback-function/5207/6 "2018-11-27T02:09:50Z")

</div>

```
return setTimeout(function() {newSetInterval.apply(scope, args);}, duration);
```

---

<div class="post-metadata">

**Author:** ![abajolle](https://avatars.discourse-cdn.com/v4/letter/a/df705f/32.png) [@abajolle](https://discourse.nodered.org/u/abajolle)\
**Post date:** [27 November 2018 02:29 UTC](https://discourse.nodered.org/t/settimeout-issue-on-a-node-with-callback-function/5207/7 "2018-11-27T02:29:14Z")

</div>

I have modified the function as per below, but the behaviour is still the same after a reboot.

```auto
  function newSetInterval(callback, duration, callbackArguments)
	{
    callback.apply(this, callbackArguments);
    var args = arguments,
    scope=this;
    return setTimeout(function() {newSetInterval.apply(scope, args);}, duration);
	}

```

If I deploy without rebooting I get multiple reads. The missing return was a clear reason not to work I got that now, so maybe it seems clearTimeout is not implemented correctly.

---

<div class="post-metadata">

**Author:** ![nlecaude](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/nlecaude/32/25_2.png) [@nlecaude](https://discourse.nodered.org/u/nlecaude)\
**Post date:** [27 November 2018 02:37 UTC](https://discourse.nodered.org/t/settimeout-issue-on-a-node-with-callback-function/5207/8 "2018-11-27T02:37:23Z")

</div>

From what I understand your setTimeout calls itself so it repeats, why not avoid all this and use setInterval (and clearInterval) instead?

---

<div class="post-metadata">

**Author:** ![abajolle](https://avatars.discourse-cdn.com/v4/letter/a/df705f/32.png) [@abajolle](https://discourse.nodered.org/u/abajolle)\
**Post date:** [27 November 2018 02:45 UTC](https://discourse.nodered.org/t/settimeout-issue-on-a-node-with-callback-function/5207/9 "2018-11-27T02:45:00Z")

</div>

The initial node were using setInterval/clearInterval, but they bug, and crash Node-RED within minutes of use. I saw that a generic issue with setInterval was due to the function running longer than the interval time, hence I try to add a callback, and since I added it I have no more problem like that, it works perfectly but needs a restart.

```auto
var sys = require('util')
var exec = require('child_process').exec;

module.exports = function(RED) {
    var mraa = require('mraa');
    
	exec("echo 63 > /sys/class/gpio/export");
    function gpioDin(n) {
        RED.nodes.createNode(this,n);
        var node = this;
		node.pin = n.pin;
		node.updateInterval = n.updateInterval;
        node.mGpio = new mraa.Gpio(parseInt(node.pin));
        node.mGpio.mode(mraa.PIN_GPIO);
        node.mGpio.dir(mraa.DIR_IN);
		var lastPinState = node.mGpio.read();
        var currentPinState = 0;
                
        var timerId = setInterval(function()
{
			if(node.pin != "ub")
			{			
				currentPinState = node.mGpio.read();
			}
			else
			{
				exec("cat /sys/class/gpio/gpio63/value", function(error, stdout, stderr) {
					currentPinState = parseInt(stdout);
				});
			}
			if(currentPinState == 0 || currentPinState == 1)
			{
				if (currentPinState != lastPinState)
				{
					var msg = { payload:currentPinState, topic:"D"+node.pin };
					node.send(msg);
				}
				lastPinState = currentPinState;
			}
		},parseInt(node.updateInterval));   
		
		this.on('close', function() {
			clearInterval(timerId);
		});
	}	
    RED.nodes.registerType("IoT2000-gpio-din", gpioDin);

```

So I am not sure which direction to go to solve this issue.

---

<div class="post-metadata">

**Author:** ![abajolle](https://avatars.discourse-cdn.com/v4/letter/a/df705f/32.png) [@abajolle](https://discourse.nodered.org/u/abajolle)\
**Post date:** [29 November 2018 07:06 UTC](https://discourse.nodered.org/t/settimeout-issue-on-a-node-with-callback-function/5207/10 "2018-11-29T07:06:13Z")

</div>

Hi,

I have figured it out, here is the modified node:

```auto
var sys = require('util')
var exec = require('child_process').exec;

module.exports = function(RED) 
{
  var mraa = require('mraa');
/ **************************************************** /
/* New functions */
/ **************************************************** /
function newSetInterval(func, interval)
{
    newSetInterval.Ids = newSetInterval.Ids || {};
    newSetInterval.IdCount = newSetInterval.IdCount || 0;
    var That = this;
	var Id = newSetInterval.IdCount++;
    var FFBug = arguments.length - 2;//To prevent Firefox bug that adds an extra element to the arguments
    (
		function Fn()
		{
			var args = [].slice.call(arguments, 0, FFBug);//To prevent Firefox bug that adds an extra element to the arguments
			func.apply(this, args);
			newSetInterval.Ids[Id] = setTimeout.apply(this, [Fn, interval].concat(args));
		}
	).apply(That, [].slice.call(arguments, 2, arguments.length));
    return Id;
}
function clearInterval(Id)
{
    if(!newSetInterval.Ids || !newSetInterval.Ids[Id])
	{
        return false;
    }
    clearTimeout(newSetInterval.Ids[Id]);
    return true;
}
/ **************************************************** /
  exec("echo 63 > /sys/class/gpio/export");
  function gpioDin(n) {
    RED.nodes.createNode(this,n);
    var node = this;
    node.pin = n.pin;
    node.updateInterval = n.updateInterval;
    node.mGpio = new mraa.Gpio(parseInt(node.pin));
    node.mGpio.mode(mraa.PIN_GPIO);
    node.mGpio.dir(mraa.DIR_IN);
    var lastPinState = 0;
    var currentPinState = node.mGpio.read();
    var timerId = newSetInterval(function()
		{
			if(node.pin != "ub")
			{			
				currentPinState = node.mGpio.read();
			}
			else
			{
				exec("cat /sys/class/gpio/gpio63/value", function(error, stdout, stderr) {
				currentPinState = parseInt(stdout)});
			}
			if(currentPinState === 0 || currentPinState === 1)
			{
				if (currentPinState != lastPinState)
				{
					if(currentPinState === 0)
						node.status({fill:"grey",shape:"ring",text:"Low"});
					else if(currentPinState === 1)
						node.status({fill:"grey",shape:"dot",text:"High"});
					var msg = { payload:currentPinState, topic:"D"+node.pin };
					node.send(msg);
				}
				lastPinState = currentPinState;
			}
		},parseInt(node.updateInterval));   
		
		this.on('close', function() {
			clearInterval(timerId);
		});
	}	
  RED.nodes.registerType("IoT2000-gpio-din", gpioDin);
}

```

This works perfectly in case someone else ever need the same thing.
