Error in a function in Node-Re 2.2 not present in previous versoins

Thanks for that early-morning response Nick (assuming you're in GMT+0 timezone or similar). I'm actually quite excited abuoit the changes in V2.2 and being made aware of the context data tab which somehow I'd missed. what was all that about with the brackets on your first line..

Ok, I'm happy with

let timing = RED.util.cloneMessage(global.get("timing"))

for a clone assuming it's scope is the local function?

And am I right in saying that to refer to the actual global I can do:

let timing=global.get("timing");

again local to my function?
in which case I stupidly then went on to update the global when I could have referred to timing= [blah,blah];

all ok?

When you change the global you should still do global.set() at some point in the function so that node-red knows you have changed it. In fact it is not necessary when using in-memory context, but if you are using file storage the the set function tells node red that it needs to write it out to disc at the appropriate moment. Otherwise your change might not persist over a restart.

4 Likes

Yes, that's correct. Cloning JavaScript objects can be a bit of an art as you get into shallow and deep clones :slight_smile: Mostly you don't need to worry and can simple use the nice shortcut function that Node-RED provides.

What I meant by my earlier comment was that the word "timing" (which, by the way, even you mistyped as "timeing" - something that I might also do and indicates maybe not a good word to use as a var name) is very generic. something like myThingsTiming - e.g. temperatureSensorTiming or whatever - is a much better name for a variable, especially one that you have made global since in a few months time you will likely scratch your head for several minutes wondering what the real scope of that variable is.

That is a shortcut to creating a clone of an array. Using the spread operator ... to "spread" the elements of the source array into a new array defined as [ ].

What's wrong with var?

There are plenty of discussions around var/let/const available online. Some people get very passionate about it. Ultimately it comes down to scope - just how visible a var variable is compared to let. Rather than repeat all the discussion points, here's a good article that explains the difference.

1 Like

Ah, but. that article makes the scopes clear but doesn't seem to cover the use of LET when referring to an existing global variable. If I'm right we just agreed that unlike var it becomes a reference to the original var unless we specifically refer to it as a clone - but that take me back to another language that used to give me problems,

SO in the case of my badly named global array timing, if I want to be lazy in it's use I can say:
let timing=global.get("timing")
and I'll be working on the original. But does that only refer to strings and arrays - what about numbers?
let mynumber=global.get("mynumber")
Same?

It only applies to Arrays and Objects.

Strings, Numbers and Booleans can be changed locally without modifying the global.

Consider this code:

let myArray = [ 1, 2, 3 ]

This will create a new Array object in memory and myArray will be a pointer to it in memory.

If you do operations that modify the contents of the array such as:

myArray[1] = 4
myArray.push(1)

then you are modifying the array held in memory.

But if you do:

let myArray = [ 1, 2, 3 ]
myArray = [ 2, 3, 4 ]

then you are creating a new array object in memory and changing what myArray points to. This is a reassignment - it does not change the original array.

Now lets consider strings, numbers and booleans,

let myString = "foo"

Strings, numbers and booleans are called immutable types. They cannot be changed. Any operation you do on a variable of that type will reassign the variable to point to the new value. When you do something like:

myString =  myString + " bar"

You are reassigning myString to point to a different String value. The original string foo has not been changed.


This all applies to working with context in Node-RED.

let timing = global.get("timing")

Is creating a locally scoped variable pointing to the value of the global context value.

If it is an Array or Object and you modify the contents of that object, you modify the value in context.
If it is a String, Number or Boolean and you change its value, you are reassigning what the local variable points at and the context value is untouched.

1 Like

There is no difference between let and var for that. The difference is purely in the scoping.

1 Like

JavaScript is rather like playing the guitar - deceptively "simple"! :sunglasses:

1 Like

Ok, now you've got me pondering whether to take up tree felling - this is getting too complicated. "To LET or not to LET.... whether 'tis nobler..... etc".. the word CONTEXT is driving me nuts.

So in short I AM modifying my original array - BUT - I have to poke in "global.set() anyway at some point.

"Is creating a locally scoped variable pointing to the value of the global context value."

That make it sound like updating for example "timing[4]+=56;" is initially reading the global but is not actually updating the global......

Yup, - tree-felling has to be easier. I'd just go and experiment but if I screw it up I could end up freezing to death :slight_smile:

Yes.

You might like to consider a pattern that I often use, which is to group all related context data into one object. Then in the function node I would have something like

let data = global.get("data")
...
if (data.someVar == ...)
...
data.someVar = someNewValue
...
global.set(data)
return msg

So I start the function with a get(), use the variable as I go along, then right at the end global.set() it again.
The only time this might involve some overheads is if the data does not usually change within the flush period specified for the context, and file based context is in use. The not-required call of global.set() is not itself an issue as the overhead in that is trivial, but if using file based context then this will (I think) force a write to the file system at whatever rate is specified in settings.js (or the default value). However, even the overhead of writing it out every few minutes is likely to be negligible.

No. That is updating the global. It is not changing what timing points at. It is changing the value inside the array that timing points at.

Thanks for this Nick - but reading back over this lot has just opened up another can of worms - COLIN uses and exampleglobal.set() and elsewhere has used global.set(data).

I was only aware of global.set("x",y); or global.set("x","y") ; where the value is a string. yet here I'm seeing global.set(data) with no value and no quotes around data and global.set() with not even an object or array etc. Was I supposed to take these literally?

When Colin's said use global.set() ...

... it was only to inform you that it should be called (with your values) to ensure file context is written.

To be more explicit, you never just call global.set(), you would actually call global.set("timing", timing) to cause any changes to be written to file

Thank you - another of life's mysteries solved. That's how I thought it worked... so I still have to treat let timing=global.get("timing") as if it were a copy then - if not there would be no need for global.set("timing",timing)....

let timing = global.get("timing") does not give you a copy, it gives you a reference (pointer) to the object. If you want a copy you must clone it.

[Edit] The reason you still have to do global.set is so that node-red knows you have changed it and knows to flush it out to disc when appropriate (if it is in file storage). If you don't do the set it may not get flushed.

1 Like

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