Unexpected Behavior With Context v19.2

I tried to search for a similar issue but did not find anything.

I have always thought that in order to change a flow context variable, you must use flow.set('myVar',123). It seems that in my instance, that is not true.

For example,

[{"id":"369d49d.b1e6fb6","type":"inject","z":"4854c08e.70fde","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":290,"y":220,"wires":[["61e86ee.a3ceb9"]]},{"id":"61e86ee.a3ceb9","type":"function","z":"4854c08e.70fde","name":"Initialize Flow Context Variables","func":"let var1 = flow.get('var1') || []\nlet var2 = flow.get('var2') || []\nlet var3 = flow.get('var3') || []\nlet var4 = flow.get('var4') || []\n\nlet data = [1,2,3,4,5,6,7,8,9,10]\n\nvar1 = []\nvar2 = []\nvar3 = []\nvar4 = []\nSetVar()\n\nvar1.push(data)\nvar2.push(data)\nvar3.push(data)\nvar4.push(data)\nSetVar()\n\nfunction SetVar () {\nflow.set('var1',var1)\nflow.set('var2',var2)\nflow.set('var3',var3)\nflow.set('var4',var4)\n}\n\nreturn msg;","outputs":1,"noerr":0,"x":530,"y":220,"wires":[[]]},{"id":"b46eb986.d01ba8","type":"inject","z":"4854c08e.70fde","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":290,"y":270,"wires":[["bc80c919.7da3c8"]]},{"id":"bc80c919.7da3c8","type":"function","z":"4854c08e.70fde","name":"Splice A Var","func":"let var1 = flow.get('var1')\nlet var2 = flow.get('var2')\nlet var3 = flow.get('var3')\nlet var4 = flow.get('var4')\n\nmsg.splicedVar = var1.splice(0,1)\n\n//flow.set('var1',var1)\n\nreturn msg;","outputs":1,"noerr":0,"x":470,"y":270,"wires":[["d8fdde53.579a2"]]},{"id":"d8fdde53.579a2","type":"debug","z":"4854c08e.70fde","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","x":650,"y":270,"wires":[]},{"id":"e07232a0.7e3de","type":"catch","z":"4854c08e.70fde","name":"","scope":null,"x":280,"y":170,"wires":[["a420a85a.404418"]]},{"id":"a420a85a.404418","type":"debug","z":"4854c08e.70fde","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","x":440,"y":170,"wires":[]}]

I can initialize a flow context variable then in another function get the same flow variable, modify the local variable, and without calling flow.set the variable will change. Is this how it is supposed to function?

Another weird thing is if I create 4 flow context variables and set them to four local variables.
I can change ONE of the variables, and all four will change in the context without calling flow.set.

[{"id":"369d49d.b1e6fb6","type":"inject","z":"4854c08e.70fde","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":290,"y":220,"wires":[["61e86ee.a3ceb9"]]},{"id":"61e86ee.a3ceb9","type":"function","z":"4854c08e.70fde","name":"Initialize Flow Context Variables","func":"let var1 = flow.get('var1') || []\nlet var2 = flow.get('var2') || []\nlet var3 = flow.get('var3') || []\nlet var4 = flow.get('var4') || []\n\nlet data = [1,2,3,4,5,6,7,8,9,10]\n\nvar1 = []\nvar2 = []\nvar3 = []\nvar4 = []\nSetVar()\n\nvar1.push(data)\nvar2.push(data)\nvar3.push(data)\nvar4.push(data)\nSetVar()\n\nfunction SetVar () {\nflow.set('var1',var1)\nflow.set('var2',var2)\nflow.set('var3',var3)\nflow.set('var4',var4)\n}\n\nreturn msg;","outputs":1,"noerr":0,"x":530,"y":220,"wires":[[]]},{"id":"b46eb986.d01ba8","type":"inject","z":"4854c08e.70fde","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":290,"y":270,"wires":[["bc80c919.7da3c8"]]},{"id":"bc80c919.7da3c8","type":"function","z":"4854c08e.70fde","name":"Splice A Var","func":"let var1 = flow.get('var1')\nlet var2 = flow.get('var2')\nlet var3 = flow.get('var3')\nlet var4 = flow.get('var4')\n\nvar1[0].splice(0,1)\n\nvar t = [1,2,3,4,5,6,7,8,9,10]\nt.splice(0,1)\nmsg.t = t\n\nnode.send({var1})\n\n//flow.set('var1',var1)\n\nreturn msg;","outputs":1,"noerr":0,"x":470,"y":270,"wires":[["d8fdde53.579a2"]]},{"id":"d8fdde53.579a2","type":"debug","z":"4854c08e.70fde","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","x":650,"y":270,"wires":[]},{"id":"e07232a0.7e3de","type":"catch","z":"4854c08e.70fde","name":"","scope":null,"x":280,"y":170,"wires":[["a420a85a.404418"]]},{"id":"a420a85a.404418","type":"debug","z":"4854c08e.70fde","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","x":440,"y":170,"wires":[]},{"id":"10ec1c15.2ee704","type":"change","z":"4854c08e.70fde","name":"Delete Flow Context","rules":[{"t":"delete","p":"var1","pt":"flow"},{"t":"delete","p":"var2","pt":"flow"},{"t":"delete","p":"var3","pt":"flow"},{"t":"delete","p":"var4","pt":"flow"}],"action":"","property":"","from":"","to":"","reg":false,"x":500,"y":360,"wires":[[]]},{"id":"5df1661b.eaab38","type":"inject","z":"4854c08e.70fde","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":290,"y":360,"wires":[["10ec1c15.2ee704"]]}]

I can replicate this in both v19.2 and v20.7 using the same flow running on a RPi 3B+ Raspbian Stretch v9.4.
Node v8.11.4

Ok, so this is my fault.

In Javascript, there are mutable and immutable objects. An array is a mutable object, which means that if you do this in one function:

let localVar = flow.get('var1') || []
let data = [1,2,3,4,5,6,7,8,9,10]
localVar.push(data)
flow.set('var1',localVar)

Then do this in another function:

let localVar = flow.get('var1')
localVar[0].splice(0,1)

You would actually be changing both the localVar and var1 without calling flow.set('var1',localVar).
Because when using = to set variables from mutable objects, you are only referencing the source object. Any change made to the local, will also be made to the source.

Armed with this new knowledge, the flow works correctly now.

[{"id":"369d49d.b1e6fb6","type":"inject","z":"4854c08e.70fde","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":940,"y":310,"wires":[["61e86ee.a3ceb9"]]},{"id":"61e86ee.a3ceb9","type":"function","z":"4854c08e.70fde","name":"Initialize Flow Context Variables","func":"let var1 = flow.get('var1') || []\nlet var2 = flow.get('var2') || []\nlet var3 = flow.get('var3') || []\nlet var4 = flow.get('var4') || []\n\nlet data = [1,2,3,4,5,6,7,8,9,10]\n\nvar1 = []\nvar2 = []\nvar3 = []\nvar4 = []\nSetVar()\n\nvar1.push(data)\nvar2.push(data)\nvar3.push(data)\nvar4.push(data)\nSetVar()\n\nfunction SetVar () {\nflow.set('var1',var1)\nflow.set('var2',var2)\nflow.set('var3',var3)\nflow.set('var4',var4)\n}\n\nreturn msg;","outputs":1,"noerr":0,"x":1180,"y":310,"wires":[[]]},{"id":"b46eb986.d01ba8","type":"inject","z":"4854c08e.70fde","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":940,"y":360,"wires":[["bc80c919.7da3c8"]]},{"id":"bc80c919.7da3c8","type":"function","z":"4854c08e.70fde","name":"Splice A Var","func":"let var1 = flow.get('var1')\nlet var2 = flow.get('var2')\nlet var3 = flow.get('var3')\nlet var4 = flow.get('var4')\n\n//var1[0].splice(0,1) <- changes flow context too!\n\nvar t = Array.from(var1[0]) // <- wont change flow context!\nt.splice(0,1)\n\nmsg.t = []\nmsg.t.push(t)\n\nflow.set('var1',msg.t)\n\nreturn msg;","outputs":1,"noerr":0,"x":1120,"y":360,"wires":[["d8fdde53.579a2"]]},{"id":"d8fdde53.579a2","type":"debug","z":"4854c08e.70fde","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","x":1300,"y":360,"wires":[]},{"id":"e07232a0.7e3de","type":"catch","z":"4854c08e.70fde","name":"","scope":null,"x":930,"y":260,"wires":[["a420a85a.404418"]]},{"id":"a420a85a.404418","type":"debug","z":"4854c08e.70fde","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","x":1090,"y":260,"wires":[]},{"id":"10ec1c15.2ee704","type":"change","z":"4854c08e.70fde","name":"Delete Flow Context","rules":[{"t":"delete","p":"var1","pt":"flow"},{"t":"delete","p":"var2","pt":"flow"},{"t":"delete","p":"var3","pt":"flow"},{"t":"delete","p":"var4","pt":"flow"}],"action":"","property":"","from":"","to":"","reg":false,"x":1150,"y":450,"wires":[[]]},{"id":"5df1661b.eaab38","type":"inject","z":"4854c08e.70fde","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":940,"y":450,"wires":[["10ec1c15.2ee704"]]}]

A site that explains it well
https://www.samanthaming.com/tidbits/35-es6-way-to-clone-an-array

2 Likes