Function with two outputs

Dear colleagues
I'm not new to node-red, but I'm no programming experts neither.
While working on a flow, I tried to figure out how to create more than one message in a function node. I did read the documentation about the function node. And I thought I understood. But a very small tests showed me, that I don't.
Here is the test. I would be grateful for an explanation, why I can't duplicate an incoming message, change payload and topic, and create two different outgoing messages.

[{"id":"87c6db9f.4b9aa8","type":"inject","z":"b46b7fa0.7b04d","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"test1","payload":"1","payloadType":"num","x":320,"y":340,"wires":[["702af256.a9349c","e1e2c7bb.a1d0f8"]]},{"id":"702af256.a9349c","type":"function","z":"b46b7fa0.7b04d","name":"worsk as expected","func":"var msg1 = {};\nmsg1.topic = \"test2\";\nmsg1.payload = 2;\nreturn [msg, msg1];","outputs":2,"noerr":0,"initialize":"","finalize":"","x":540,"y":340,"wires":[["615b50d6.65b8f"],["7ac6ed31.699dc4"]]},{"id":"615b50d6.65b8f","type":"debug","z":"b46b7fa0.7b04d","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":790,"y":320,"wires":[]},{"id":"7ac6ed31.699dc4","type":"debug","z":"b46b7fa0.7b04d","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":790,"y":360,"wires":[]},{"id":"e1e2c7bb.a1d0f8","type":"function","z":"b46b7fa0.7b04d","name":"works in mysterious ways","func":"var msg1 = msg;\nmsg1.topic = \"test2\";\nmsg1.payload = 2;\nreturn [msg, msg1];","outputs":2,"noerr":0,"initialize":"","finalize":"","x":540,"y":420,"wires":[["db315194.668608"],["4b1a05c.59d27fc"]]},{"id":"db315194.668608","type":"debug","z":"b46b7fa0.7b04d","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":790,"y":400,"wires":[]},{"id":"4b1a05c.59d27fc","type":"debug","z":"b46b7fa0.7b04d","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":790,"y":440,"wires":[]}]

This is a case in Javascript where its called 'assigment by reference'
when you do var msg1 = msg; and change the values, msg1 is simply a pointer in memory to msg and you modify this way the original message.
This i believe applies to object and arrays and you wll not see the same with premitive variables like numbers, strings

[EDIT] you can avoid it - pointing to the same memory and hence when you change the new msg in essence changing the original one too by using the spread operator ...

var msg1 = {...msg};

There a nice video by dcode that better explains and demonstrates this
Passing by reference vs. by value

The spread operator will not do a deep clone so this will only work if your object you are cloning is only one layer deep.
Node-red actually provides cloning functionality in the form of

var newMsg = RED.util.cloneMessage(msg))

which uses one of the available npm modules for cloning.
You can also use the hackish way which is:

var newMsg = JSON.parse(JSON.stringify(msg))

Johannes

Edit:
You can read more about it here


Dont know if the nodered functionality isnt also based on lodash which is mentioned in this article.
1 Like

Watch out for that. It is very fragile. Really, you should always wrap the JSON functions in a try/catch

var newMsg = {}
try {
    newMsg = JSON.parse(JSON.stringify(msg))
catch {}
1 Like

:+1:t2: Yes i m aware, thats why i called it hackish and linked the article.

Thanks, I'll go with the node-red way of cloning a message.
This by-reference stuff freaks me out, you never know what you have and some other process might change what you think you have. I'm too old for this.
(BTW: in my coding for my ESP based sensors (Arduino IDE) I very often make Strings (upper case) out of values, just to make sure that I have my own thing in my hand and not just a lousy pointer to a place in the memory.)
Thanks again, cloning is the way forward.

Well, the advantages of references vs copies is clear and mostly works in our favour. Node-RED based on constant copies of data being passed between nodes would be horrible. And mostly it "just works". Async programming can be a pain though for that odd occasion when something gets changed and flows through all the references at the "wrong time" in your nice linear logic :unamused: