Delay - wait in loop/function

Hi!

I need to make a slot machine effect
So a running light over 4 outputs the goes slower and slower
I thought to do this in a loop but i cannot get the delay working.

for example i did this:

msg.payload = "test1"
node.send(msg);

delay 1000;

msg.payload = "test2"
node.send(msg);

delay 1000;

msg.payload = "test3"
node.send(msg);

But that gives errors.
Is that possible to do?
I found some posts with the timeout function but have no idea how to include this in a loop

Is it possible to use delay or wait or something?

Hello,
There is no native delay or wait function in javascript so this approach will not work like it would in c or python. You most likely will have to approach it differently. Some ideas I have are you could try move the delays outside of the function node and use something like the trigger or delay node to achieve your timeout and feedback into the function from its output, this would be utilizing a very nodered way of doing things. Another option would be to read more about the setTimeout method in JavaScript to achieve it.
For the first idea you could utilize a node context variable to keep track of how many loops/round trips you have run of the function and the delay node. Here is a simple example of this approach:

[{"id":"5331bb12.a5ac24","type":"tab","label":"Flow 3","disabled":false,"info":""},{"id":"fdff5d7d.9e8ce8","type":"inject","z":"5331bb12.a5ac24","name":"Number of loops","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"4","payloadType":"num","x":270,"y":140,"wires":[["45f326c3.1fde28"]]},{"id":"45f326c3.1fde28","type":"function","z":"5331bb12.a5ac24","name":"","func":"let loops = context.get(\"loops\") || 1;\nif (msg.topic !== \"running\"){\n    context.set(\"loops\", msg.payload - 1);\n    msg.topic = \"running\";\n    return [msg, msg]\n} else if (loops !== 1) {\n    msg.payload = loops;\n    context.set(\"loops\", loops - 1);\n    return [msg, msg];\n} else {\n    msg.payload = loops;\n    return [msg, null];\n}\nreturn msg;","outputs":2,"noerr":0,"initialize":"","finalize":"","x":480,"y":140,"wires":[["cbc26d74.e76d08"],["63da4291.62cc44"]]},{"id":"63da4291.62cc44","type":"delay","z":"5331bb12.a5ac24","name":"","pauseType":"delay","timeout":"1","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":480,"y":200,"wires":[["45f326c3.1fde28"]]},{"id":"cbc26d74.e76d08","type":"debug","z":"5331bb12.a5ac24","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":670,"y":140,"wires":[]}]

Maybe this can give you some ideas.

Johannes

1 Like

Another idea

[{"id":"949f8f0b.827c38","type":"tab","label":"Delay","disabled":false,"info":""},{"id":"e68e112c.5b28f8","type":"inject","z":"949f8f0b.827c38","name":"Go","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":330,"y":300,"wires":[["dc6090a3.f445","d3b39a35.693538","474719d8.2d241"]]},{"id":"dc6090a3.f445","type":"function","z":"949f8f0b.827c38","name":"Test 1","func":"msg.payload = 'Test 1';\n\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":530,"y":240,"wires":[["223d668e.2dddda"]]},{"id":"223d668e.2dddda","type":"delay","z":"949f8f0b.827c38","name":"","pauseType":"delay","timeout":"1","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":670,"y":240,"wires":[["b2487ed2.86f138"]]},{"id":"b2487ed2.86f138","type":"function","z":"949f8f0b.827c38","name":"Other Functions","func":"\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":930,"y":300,"wires":[["b8394c14.1b7858"]]},{"id":"d3b39a35.693538","type":"function","z":"949f8f0b.827c38","name":"Test 2","func":"msg.payload = 'Test 2';\n\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":530,"y":300,"wires":[["379007f8.ce411"]]},{"id":"379007f8.ce411","type":"delay","z":"949f8f0b.827c38","name":"","pauseType":"delay","timeout":"2","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":670,"y":300,"wires":[["b2487ed2.86f138"]]},{"id":"474719d8.2d241","type":"function","z":"949f8f0b.827c38","name":"Test 3","func":"msg.payload = 'Test 3';\n\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":530,"y":360,"wires":[["1551632a.4fb295"]]},{"id":"1551632a.4fb295","type":"delay","z":"949f8f0b.827c38","name":"","pauseType":"delay","timeout":"3","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":670,"y":360,"wires":[["b2487ed2.86f138"]]},{"id":"b8394c14.1b7858","type":"debug","z":"949f8f0b.827c38","name":"Final","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1130,"y":300,"wires":[]}]
1 Like

I think this approach although very simple might get messy very quickly if you try to apply it to more complex examples which require a longer loop and maybe although a variable changing delay which I think is what @RogierQ will need for his slot machine but it definitely is a quick workaround :+1::blush:

Very quick solution, indeed.
But for 3 or 4 light bulbs it's simple enough. For multiple bulbs, not applicable.

Thanks for your ideas!
Only thing is that in the examples the delay is static, but the delay needs to get longer after every step.
so it starts fast and stops slowerrrrr
I cannot set the timing parameter with a external value from the script

Take a look :

1 Like

You can set the delay by adding a msg.delay property to the msg you send from the function node and setting the delay node accordingly.
Here is the same example with the delay being set from the function node and getting longer:

[{"id":"5331bb12.a5ac24","type":"tab","label":"Flow 3","disabled":false,"info":""},{"id":"fdff5d7d.9e8ce8","type":"inject","z":"5331bb12.a5ac24","name":"Number of loops","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"4","payloadType":"num","x":270,"y":140,"wires":[["45f326c3.1fde28"]]},{"id":"45f326c3.1fde28","type":"function","z":"5331bb12.a5ac24","name":"","func":"let loops = context.get(\"loops\") || 1;\nlet delay = context.get(\"delay\");\nif (msg.topic !== \"running\"){\n    context.set(\"loops\", msg.payload - 1);\n    msg.topic = \"running\";\n    msg.delay = 1000;\n    context.set(\"delay\", msg.delay);\n    return [msg, msg]\n} else if (loops !== 1) {\n    msg.payload = loops;\n    msg.delay = delay + 500;\n    context.set(\"loops\", loops - 1);\n    context.set(\"delay\", msg.delay);\n    return [msg, msg];\n} else {\n    msg.payload = loops;\n    return [msg, null];\n}\nreturn msg;","outputs":2,"noerr":0,"initialize":"","finalize":"","x":480,"y":140,"wires":[["cbc26d74.e76d08"],["63da4291.62cc44"]]},{"id":"63da4291.62cc44","type":"delay","z":"5331bb12.a5ac24","name":"","pauseType":"delayv","timeout":"1","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":480,"y":200,"wires":[["45f326c3.1fde28"]]},{"id":"cbc26d74.e76d08","type":"debug","z":"5331bb12.a5ac24","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":670,"y":140,"wires":[]}]

You should be able to simulate a delay (without blocking Node-RED from processing other things meanwhile) by using async/await:

async function delay(timeMs) {
  await new Promise(resolve => setTimeout(resolve, timeMs));
}

msg.payload = "test1"
node.send(msg);

await delay(1000);

msg.payload = "test2"
node.send(msg);

(I didn't test this)

1 Like

i was just recently studying about promises and the syntax is to short to fully understand the resolve

but shouldnt that be return new Promise(resolve => setTimeout(resolve, timeMs));

It might not make a difference in this case. As the async delay() returns a Promise. The Promise is resolved when the part of the function that awaits the manually created promise to resolve (after the timeout).

But if both work, your suggestion would avoid rhe second promise layer. TBH I just did a quick search to find a confirmation to what I had seen before and modified the first result to fit this use case as I'm on my tablet.

The async/await syntax is just "syntactic sugar" to make promise handling look more like synchronous code execution and it actually comes with some overhead (likely not something that would matter in most cases).

Something I wrote got mentioned back in post 6.

The problem if you use that is that the delay would have to start off small and become bigger.

But it seems like you hare making good progress on it.

Good luck.