Delay in function v1.0.2

I thought that i saw some were in a release node that it is possible now to set a delay time in a function node in a loop. But i cannot find it anymore. Am i wrong?
With google i found this, but i cannot get it to work

The result that i am looking for should be:
t
delay 1000
te
delay 1000
tex
delay 1000
and so on

[{"id":"21d6512d.38cefe","type":"inject","z":"4657ec75.580b0c","name":"","topic":"","payload":"text input ","payloadType":"str","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":160,"y":120,"wires":[["e2a77a97.2217a8"]]},{"id":"e2a77a97.2217a8","type":"function","z":"4657ec75.580b0c","name":"","func":"var p = msg.payload;\n\nvar i = p.length;\n\n\nfunction myLoop(){\n    \n    setTimeout(function () {\n        \n    for (i = 0; i < p.length; i++) { \n    var first = p.substring(0, i);\n    msg.payload = first;\n    node.send(msg);\n    \n    }\n}, 1000 );\n}\n\nmyLoop();","outputs":1,"noerr":0,"x":360,"y":120,"wires":[["340c431b.65bbe4"]]},{"id":"340c431b.65bbe4","type":"debug","z":"4657ec75.580b0c","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":530,"y":120,"wires":[]}]
1 Like

Try this:

[{"id":"2241383.07a83c8","type":"inject","z":"4f466ce8.916f14","name":"","topic":"","payload":"This is a test","payloadType":"str","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":214,"y":264,"wires":[["27283675.fe434a"]]},{"id":"27283675.fe434a","type":"split","z":"4f466ce8.916f14","name":"","splt":"1","spltType":"len","arraySplt":1,"arraySpltType":"len","stream":false,"addname":"","x":362,"y":264,"wires":[["e9e134e9.8c151"]]},{"id":"34b19c43.7cf71c","type":"debug","z":"4f466ce8.916f14","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":682,"y":264,"wires":[]},{"id":"e9e134e9.8c151","type":"delay","z":"4f466ce8.916f14","name":"","pauseType":"rate","timeout":"1","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":514,"y":264,"wires":[["34b19c43.7cf71c"]]}]

@RogierQ what you may have seen is it is possible to dynamically set the delay applied to a message by the Delay node.

The Function node has always had the ability to set setTimeout/setInterval - although it is usually not the best "node-red" way of doing things.

The flow that @bakman2 has provided could really do with some explanation to go along with it. It shows how you could pass an array of elements to the split node, that will turn that into one message per element, and then a delay node set to rate-limit them to 1 per second.

Whether that is suitable depends on the details of what you want to do. Do you want a certain number of messages to be sent? Or do you want it to keep sending messages every second for ever? (If that is the case, you could use a Inject node to repeat at whatever interval you want).

Thanks @bakman2 and @knolleary!
what i try to create is a typewriter effect from incoming messages. Mostly a complete sentence.
the example from @bakman2 does the job. only need to combine the characters then after every output but that is not a problem to do

When i set the timing of the delay node below one second it becomes bumpy.
The delay varies.
And one second is like my parents are tiping :wink: thats a little slow...
See my test below

[{"id":"c76aac1a.cdbf38","type":"inject","z":"49097f11.334d2","name":"","topic":"","payload":"This is a test, This is a test, This is a test, This is a test, This is a test, This is a test, This is a test, This is a test, ","payloadType":"str","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":170,"y":240,"wires":[["957d79c1.ddddf8"]]},{"id":"d0d7e8b3.7c8818","type":"split","z":"49097f11.334d2","name":"","splt":"1","spltType":"len","arraySplt":1,"arraySpltType":"len","stream":false,"addname":"","x":438,"y":240,"wires":[["8cd9d7fb.f84bb"]]},{"id":"8cd9d7fb.f84bb","type":"delay","z":"49097f11.334d2","name":"","pauseType":"rate","timeout":"1","timeoutUnits":"seconds","rate":"1","nbRateUnits":"0.2","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":590,"y":240,"wires":[["80670290.168fc"]]},{"id":"80670290.168fc","type":"function","z":"49097f11.334d2","name":"","func":"var p = msg.payload;\n\nvar l = flow.get(\"line\");\n\nvar newLine = l + p\n\nflow.set(\"line\",newLine);\n\nmsg.payload = newLine;\n\n\nreturn msg;","outputs":1,"noerr":0,"x":750,"y":240,"wires":[["faa9da34.2f022"]]},{"id":"faa9da34.2f022","type":"debug","z":"49097f11.334d2","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":940,"y":240,"wires":[]},{"id":"957d79c1.ddddf8","type":"function","z":"49097f11.334d2","name":"","func":"\nflow.set(\"line\",\" \");\n\nreturn msg;","outputs":1,"noerr":0,"x":310,"y":240,"wires":[["d0d7e8b3.7c8818"]]}]

how are you measuring that - by watching the messages pass by in the debug output?

While I suspect nodejs is not true real-time, i'd bet what you are seeing is the debug window stuttering.

To test this theory, I did some quite rudamentory test code & this is the results...

{
"firstMsgTime":1572268888410,
"lastMsgTime":1572268913832,
"min":199,
"max":204,
"count":128,
"average":200.0703248977659,
"msSinceLast":200
}

As you can see, the quickest delay was 199ms and the longest delay was 204ms. The msgs were emitted on average every 200.07ms

The updated flow is below - try it yourself (the results can be found in the context data tab on the side-bar.

NOTE Interestingly, I did see some occasional high delays (+500ms) between 2 messages when watching the debug. However, with the debug node turned off the results shown in the context data tab were very consistent.

Updated flow with min/max/average/count etc

[{"id":"2ff489d3.ea8f96","type":"inject","z":"6d7db396.221eac","name":"","topic":"","payload":"This is a test string consisiting on blah blah blah","payloadType":"str","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":110,"y":100,"wires":[["21941de9.e7d532"]]},{"id":"3509253f.42adfa","type":"split","z":"6d7db396.221eac","name":"","splt":"1","spltType":"len","arraySplt":1,"arraySpltType":"len","stream":false,"addname":"","x":378,"y":100,"wires":[["989ebb05.2d9678"]]},{"id":"989ebb05.2d9678","type":"delay","z":"6d7db396.221eac","name":"rate limit 0.2s","pauseType":"rate","timeout":"1","timeoutUnits":"seconds","rate":"1","nbRateUnits":"0.2","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":530,"y":100,"wires":[["1f692b66.de5e35"]]},{"id":"1f692b66.de5e35","type":"function","z":"6d7db396.221eac","name":"","func":"var p = msg.payload;\nvar l = flow.get(\"line\");\nvar newLine = l + p\nflow.set(\"line\",newLine);\nmsg.payload  = newLine;\n\nvar now = Date.now();\nmsg.timestamp = now;\n\nvar record = flow.get(\"record\");\nrecord.count += 1;\nif(!record.firstMsgTime){\n    record.firstMsgTime = now;\n    msg.msSinceLast = now - record.lastMsgTime;\n    record.average = msg.msSinceLast\n} else {\n    msg.msSinceLast = msg.timestamp - record.lastMsgTime;\n    if(record.min > msg.msSinceLast) \n        record.min = msg.msSinceLast; \n    if(record.max < msg.msSinceLast) \n        record.max = msg.msSinceLast; \n    record.msSinceLast = msg.msSinceLast;\n    record.average = ((record.average + msg.msSinceLast) / 2.0)\n}\nmsg.msAverage = record.average;\nmsg.min = record.min;\nmsg.max = record.max;\nmsg.count = record.count;\nrecord.lastMsgTime = now;\nflow.set(\"record\",record);\n\nreturn msg;","outputs":1,"noerr":0,"x":550,"y":140,"wires":[["ca79e89b.51fc18"]]},{"id":"ca79e89b.51fc18","type":"debug","z":"6d7db396.221eac","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","x":570,"y":180,"wires":[]},{"id":"21941de9.e7d532","type":"function","z":"6d7db396.221eac","name":"","func":"\nflow.set(\"line\",\" \");\nflow.set(\"record\",{firstMsgTime: null, lastMsgTime: Date.now(), min: 9999999, max: -1, count: 0, average: 0})\nreturn msg;","outputs":1,"noerr":0,"x":250,"y":100,"wires":[["3509253f.42adfa"]]}]

The underlying setTimeout() function is not very accurate, so some hiccups are to be expected. Especially under system load or when intensive tasks are being processed by the event loop.

Don't forget that the hardware you are running on and what else is running on it will effect performance.

I find if I provide some basic information, I get faster responses to my queries. When I start a thread, I provide:

  • node-red version,
  • node.js version
  • dashboard version (if using it)
  • name of any contrib nodes (node-red-contrib-cron-plus for example) that I'm using
  • and explaination of any terms that are not common (What the heck is a Lwm2m server??)
  • a small test flow showing my issue.
  • a description of what I have already tried.

This way, someone looking at the thread doesn't have to ask for that information and is more apt to respond to me.

2 Likes

It absolutely isn't real-time and must never be assumed as such as that will surely get you into all kinds of timewarps! :slight_smile:

We are seeing lots of people fall into this trap in the forum. Node.js is an asynchronous looped language (e.g. it has an implicit loop unlike something like Arduino C++ which has an explicit loop). In many cases, you cannot assume that something will be processed in the current loop and you cannot make assumptions about how long a particular loop will take. Node.js also adds in its own overheads such as the garbage collection which can add maybe up to a second sometimes. Then you have the overheads of Node-RED itself which must do many tasks, not just yours and finally you are running on a non-realtime OS which means that the OS itself could add significant delays to Node-RED by tying up all of the available processor threads.

To me, it is a wonder that Node.js even approximates accurate timing! In reality, it is generally pretty good but you are relying on an awful lot of other people's code and a bunch of assumptions once you try to go for sub-second timing.

If you need to rely on millisecond level timing, you need to run a device that has a realtime OS. An ESP32 for example. Or even an Arduino for many things. And you need to code things carefully.

I've learned to play it safe on this forum (e.g. littering my statements with maybe & probably) after realising I dont know everything :wink:

Absolutely

I familiar with the NodeJS eventloop and its garbage collection etc however I was still a little surprised to see (occasionally) a 200ms delay hit 700+ ms - thats quite a leap. It did settle down somewhat when debug node was disabled. I thought I'd mention it for the OP to evaluate if node-red/nodejs was the right fit for his problem.

2 Likes

Maybe its better to change the topics subject? :wink:
The question was how to put a delay in a flow.
I never spoke about accurate timing actually its perfect to me for the typewriter effect if its not exact...
Thanks for all your help and suggestions, its working now!

No need, we just get a bit overexcited and carried away sometimes when sharing knowledge. :laughing:

The main thing is that your problem is solved. :+1:

1 Like