Will asynchronous change in a context variable with flow-scope, be immediately noticed by other nodes in the flow?

I'm new to nodered. I'd like to set a context variable with flow scope to control another node in the same page. But, I get "SyntaxError: Invalid or unexpected token (body:line 1)"

[{"id":"db8cd33d.d63fb","type":"ui_button","z":"107c0bc9.ee7e94","name":"","group":"de5434f4.427aa8","order":0,"width":"0","height":"0","passthru":false,"label":"{{payload}}","tooltip":"","color":"","bgcolor":"{{colour}}","icon":"","payload":"press","payloadType":"str","topic":"","x":850,"y":360,"wires":[["e06dd672.171ab"]]},{"id":"add82538.28a428","type":"change","z":"107c0bc9.ee7e94","name":"","rules":[{"t":"set","p":"colour","pt":"msg","to":"green","tot":"str"},{"t":"set","p":"payload","pt":"msg","to":"App1","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":670,"y":315,"wires":[["db8cd33d.d63fb"]]},{"id":"e06dd672.171ab","type":"function","z":"107c0bc9.ee7e94","name":"state2","func":"var state = flow.get(“bIsRunning”);\nstate = !state;\nflow.set(“bIsRunning”, state);\nif (state) { return [msg,null]; }\nelse { return [null,msg]; }","outputs":2,"noerr":2,"initialize":"","finalize":"","x":475,"y":360,"wires":[["add82538.28a428"],["56955565.83a8bc"]]},{"id":"56955565.83a8bc","type":"change","z":"107c0bc9.ee7e94","name":"","rules":[{"t":"set","p":"colour","pt":"msg","to":"red","tot":"str"},{"t":"set","p":"payload","pt":"msg","to":"App1 Running","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":670,"y":360,"wires":[["db8cd33d.d63fb"]]},{"id":"65ef2f61.fd3e08","type":"ui_ui-button","z":"107c0bc9.ee7e94","action":"bIsRunning","actionType":"flow","name":"","group":"de5434f4.427aa8","order":3,"width":0,"height":0,"passthru":false,"label":"KillAllApps","tooltip":"","color":"","bgcolor":"","icon":"","x":130,"y":220,"wires":[["2f44599b.0bdcf6"]]},{"id":"2f44599b.0bdcf6","type":"function","z":"107c0bc9.ee7e94","name":"state1","func":"flow.set(“bIsRunning”, true);\n// make it part of the outgoing msg object\n//msg.bIsRunning = bIsRunning;\n//return msg;\n","outputs":0,"noerr":2,"initialize":"","finalize":"","x":330,"y":220,"wires":[]},{"id":"de5434f4.427aa8","type":"ui_group","z":"","name":"pk22","tab":"23a7844a.b411c4","order":1,"disp":true,"width":"6","collapse":false},{"id":"23a7844a.b411c4","type":"ui_tab","z":"","name":"TabPk1","icon":"dashboard","disabled":false,"hidden":false}]

Looks like you have copied and pasted code with smart quotes.

“bIsRunning” is not the same as "bIsRunning"

That's why it is very important to format code on the forum properly.

in order to make code more readable and importable it is important to surround your code with three backticks
```
like this
```

You can edit and correct your post by clicking the pencil icon.

See this post for more details - How to share code or flow json

Hi Steve, Thank you for the leg up. Your comment indeed helped me solve the problems partially. Now, there is no more syntax error. But, in a function node all I have is - flow.set("bIsRunning”, false); And, it complains "Invalid properties". Request you to take one more look. My hope is - I set the flow-wide visible variable in one node, and whenever that variable changes, its new value will be visible to other nodes in the flow (tab), and will be acted upon. Here is my new flow

[{"id":"db8cd33d.d63fb","type":"ui_button","z":"107c0bc9.ee7e94","name":"","group":"de5434f4.427aa8","order":0,"width":"0","height":"0","passthru":false,"label":"{{payload}}","tooltip":"","color":"","bgcolor":"{{colour}}","icon":"","payload":"press","payloadType":"str","topic":"","x":850,"y":360,"wires":[["e06dd672.171ab"]]},{"id":"add82538.28a428","type":"change","z":"107c0bc9.ee7e94","name":"","rules":[{"t":"set","p":"colour","pt":"msg","to":"green","tot":"str"},{"t":"set","p":"payload","pt":"msg","to":"App1","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":670,"y":315,"wires":[["db8cd33d.d63fb"]]},{"id":"e06dd672.171ab","type":"function","z":"107c0bc9.ee7e94","name":"state2","func":"var state = flow.get(\"bIsRunning\");\nstate = !state;\nflow.set(\"bIsRunning\", state);\nif (state) { return [msg,null]; }\nelse { return [null,msg]; }","outputs":2,"noerr":0,"initialize":"","finalize":"","x":475,"y":360,"wires":[["add82538.28a428"],["56955565.83a8bc"]]},{"id":"56955565.83a8bc","type":"change","z":"107c0bc9.ee7e94","name":"","rules":[{"t":"set","p":"colour","pt":"msg","to":"red","tot":"str"},{"t":"set","p":"payload","pt":"msg","to":"App1 Running","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":670,"y":360,"wires":[["db8cd33d.d63fb"]]},{"id":"65ef2f61.fd3e08","type":"ui_ui-button","z":"107c0bc9.ee7e94","action":"bIsRunning","actionType":"flow","name":"","group":"de5434f4.427aa8","order":3,"width":0,"height":0,"passthru":false,"label":"KillAllApps","tooltip":"","color":"","bgcolor":"","icon":"","x":130,"y":220,"wires":[["2f44599b.0bdcf6"]]},{"id":"2f44599b.0bdcf6","type":"function","z":"107c0bc9.ee7e94","name":"state1","func":"flow.set(\"bIsRunning”, false);","outputs":0,"noerr":7,"initialize":"","finalize":"","x":330,"y":220,"wires":[]},{"id":"de5434f4.427aa8","type":"ui_group","z":"","name":"pk22","tab":"23a7844a.b411c4","order":1,"disp":true,"width":"6","collapse":false},{"id":"23a7844a.b411c4","type":"ui_tab","z":"","name":"TabPk1","icon":"dashboard","disabled":false,"hidden":false}]

image

EDIT...
PS, for a simple setting of a flow variable, you can use a simple switch node instead.

Thank you for the quick reply. With the correction you suggested, I have no more errors. But, I have not achieved the goal. When the flow-scope variable changes asynchronously, will it be acted upon by other nodes? What makes the other nodes keep looking at the flow-scope variable for any changes? Should I force other relevant nodes look at the flow-scope periodically? In my case, I changed the flow-scope variable to "false", and expected other nodes to notice the changed value and change the color of buttons.

In short, No. You must query the global/flow variable to get its current value, and then act or process that value accordingly. Check out the following for details...
https://nodered.org/docs/user-guide/context

And this is also a interesting read...

You could setup a flow to poll the value, and then take action on change of value. Others in this forum may have setup something that is a bit more elegant, but as I said, there is per my understanding no trigger on value change logic.

I believe this topic has been discussed at length in the following thread...

You could also set node status after setting context, then use status node to monitor the status value. This means no need to poll.
e.g.

[{"id":"77338bac.30b1dc","type":"inject","z":"8d22ae29.7df6d","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"false","payloadType":"bool","x":142.3333282470703,"y":2880.666748046875,"wires":[["1f57cede.9f7389"]]},{"id":"6c336e51.fd475","type":"inject","z":"8d22ae29.7df6d","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"true","payloadType":"bool","x":130,"y":2920,"wires":[["1f57cede.9f7389"]]},{"id":"1f57cede.9f7389","type":"function","z":"8d22ae29.7df6d","name":"state1","func":"flow.set(\"bIsRunning\", msg.payload);\nnode.status(msg.payload)\n    return msg;","outputs":0,"noerr":0,"initialize":"","finalize":"","x":320,"y":2920,"wires":[]},{"id":"8948bef9.e9234","type":"function","z":"8d22ae29.7df6d","name":"state2","func":"var state = flow.get(\"bIsRunning\");\nstate = !state;\nflow.set(\"bIsRunning\", state);\nif (state) { return [msg,null]; }\nelse { return [null,msg]; }","outputs":2,"noerr":0,"initialize":"","finalize":"","x":160,"y":3060,"wires":[["2aa0cf1f.c4f81"],["6b39fdae.34b64c"]]},{"id":"1efe0afb.dc8fad","type":"status","z":"8d22ae29.7df6d","name":"","scope":["1f57cede.9f7389"],"x":100,"y":3000,"wires":[["8948bef9.e9234"]]},{"id":"2aa0cf1f.c4f81","type":"change","z":"8d22ae29.7df6d","name":"","rules":[{"t":"set","p":"colour","pt":"msg","to":"green","tot":"str"},{"t":"set","p":"payload","pt":"msg","to":"App1","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":370,"y":3020,"wires":[["4cb8ad74.1f6524","d563006b.aa5ac8"]]},{"id":"6b39fdae.34b64c","type":"change","z":"8d22ae29.7df6d","name":"","rules":[{"t":"set","p":"colour","pt":"msg","to":"red","tot":"str"},{"t":"set","p":"payload","pt":"msg","to":"App1 Running","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":390,"y":3080,"wires":[["4cb8ad74.1f6524","d563006b.aa5ac8"]]},{"id":"d563006b.aa5ac8","type":"debug","z":"8d22ae29.7df6d","name":"","active":true,"tosidebar":true,"console":false,"tostatus":true,"complete":"true","targetType":"full","statusVal":"payload","statusType":"auto","x":620,"y":3000,"wires":[]},{"id":"4cb8ad74.1f6524","type":"ui_button","z":"8d22ae29.7df6d","name":"","group":"7172a8f0.cc68b8","order":0,"width":"0","height":"0","passthru":false,"label":"{{payload}}","tooltip":"","color":"","bgcolor":"{{colour}}","icon":"","payload":"press","payloadType":"str","topic":"","x":620,"y":3100,"wires":[["8948bef9.e9234"]]},{"id":"7172a8f0.cc68b8","type":"ui_group","z":"","name":"pk22","tab":"46a1b575.d8a794","order":1,"disp":true,"width":"6","collapse":false},{"id":"46a1b575.d8a794","type":"ui_tab","z":"","name":"TabPk1","icon":"dashboard","disabled":false,"hidden":false}]

If you want an action to happen when a global/flow changes state then that is often an indication that maybe a context variable is not the best solution. It may be better to wire the state directly into the node that needs it, via a Join node configured similar to this example if other data needs to be taken into account.

.. or via a pair of link nodes

1 Like

Would be nice to have a context variable refresh node, such that flow 1 sets the variable, flow 2 can invoke a refresh of the variable. This would not resolve the timing synchronization issue, but would give us a way to ensure current value is captured? If 'get' does this it is implied right?

What specifically does 'invoke a refresh' mean here?

If flow 1 sets the value to X, then the next time anything gets the value it will be X. No refreshing is needed.

I found that under the right conditions... flow 2 can get the 'old' value. Seems to be a speed issue. Given messages = events in concept... if you get a message in flow 2 that out paces flow 1 message to set the variable, it is a 'race' to what variable is set to, right?

If flow 2 reads the value before flow 1 has set it, then it will get the 'old' value. I'm not sure why that is an issue.

More of an oddity... I had a combine/completion node, and the race was won randomly by flow 2 versus flow 1, so the variable was 'inconsistent', and debugging it was nuts. I would get everything working fine for hours, then it would break. I set up a timing report, and found that flow 2 for whatever reason would once in a while win the race. Just a parallel process quirk, so when I checked the variable at the completion node unexpected result. In any multi-threading scenario this would be possible of course. But in traditional multi-threading you have ways to force branch/forks to sync back up, say with mutex for example. Two threads that update a global variable can have the same issue without a mechanism address it.

You can use setImmediate or process.nextTick so force a yield to the next time round the event loop. Which may or may not be enough to realign your paths

That is a good idea

For the initial scenario, I cheated, I dropped a short delay into the 2nd flow, that let me keep 1st flow winning the race, it was only 250ms, so barely noticeable. Fixed the issue, but really did not address the actual issue.

I think that if you find yourself having to put in bodges like that then that is a clue that using context is not the best solution. Instead you could feed the data to the nodes that need it, via Links or MQTT if appropriate, and if necessary use a Join node to combine the data with other information.

Agreed, at some point, I will get back to the flow and rework it.

This topic was automatically closed 60 days after the last reply. New replies are no longer allowed.