The nuts and bolts of JavaScript

General Add comments
by:

Today we will look into a problem that many a Servicenow consultant has lost some hours on solving. To prevent new consultants running into this problem, but also for the ones that solved it but didn’t fully comprehend the solution, here is a detailed explanation of what is going on in the nuts and bolts of JavaScript.

The problem

Suppose you are writing a function that selects the sys_id’s of users and stores them in an array. The array could be used to pass on to another function. In this example, we will query active users:

function getActiveUsers(limit){
var result = [];
var grUser = new GlideRecord('sys_user');
grUser.addQuery('active', true);

if (typeof limit === 'number'){
grUser.setLimit(limit);
}
grUser.query();
while (grUser.next()){
result.push(grUser.sys_id);
}

return result;
}

We will use a test function to show the results, limiting the result set to eight records:

function showUsers(){
var result = getActiveUsers(8);

result.forEach(function(element){
gs.print(element)
})
}

The function showUsers will print eight identical sys_id’s, rather than the eight different ones you might expect. To quote Marvin Gaye: what’s going on?
(Also note the usage of the forEach-method to iterate over the array elements, which is finally available server side from Helsinki onwards!)

Some background

Objects in JavaScript are accessible using variables. These variables do not contain objects themselves (as they do for strings, numbers and booleans), but rather a reference pointer to the memory space where the object is stored.
This can be illustrated running the following code:

var a = [1, 2];
var b = a;
gs.print(b.length);
a.push(3);
gs.print(b.length);

The first time we look at b.length, its value is 2. Without touching b however, the next time we call it, it has changed to 3. This happens because the variables are pointers. After line 1, we can picture the situation like this:

Line 2 sets b to the same value as a:

Notice that b does not point to a itself, but rather to the same object that a is pointing to.
When in line 4 an extra element is added using a.push, the situation looks like this:

Note that a and b still point to the same structure in memory, and will thus behave identical – a.length() and b.length() will yield the same result. The main thing to take away from this example is that the value of a reference variable can change even though the variable itself is untouched.

GlideRecord

In our getActiveUsers-function, we create a new instance of GlideRecord, allowing us access to the records in the sys_user table. The query method runs the query we put in place. Finally, we use next to iterate over the retrieved data.
Calling next on a GlideRecord does a number of things. It retrieves the next record from the dataset, and exposes the field values through the GlideRecord object. What is crucial here, is that even though the value of the field sys_id might give the impression of being a string, it really isn’t. All properties of a GlideRecord instance that represent fields in a table are GlideElement objects. The grUser object created using grUser = new GlideRecord(‘sys_user’) can be represented like this:

The picture shows that grUser.sys_id is a pointer to a GlideElement object. Pushing grUser.sys_id on the array during the first iteration of the while-loop then looks like this:

The first element of the array, result[0], points to the grUser object’s sys_id property, which is a GlideElement object. So far, so good.

Next iteration

The second iteration of the while-loop is started by calling grUser.next(). This loads the data for the next record in the dataset. The grUser object will remain at the same position in memory, as will all of the GlideElement-objects it refers to. Rather than destroying all GlideElements and recreating them, ServiceNow keeps the objects and fills them with the new data.
This means grUser.sys_id will yield the sys_id of the second record from the dataset, even though the GlideElement it points to is still the same. It looks like this:

The first element of the array, result[0], was not changed (just like the array b was not changed in the example above). It still points to the grUser object’s sys_id. But during the grUser.next-call, the contents of this object were changed. It now contains the sys_id of the second record from the dataset. So without actually touching result[0], its value changed nonetheless, and is now equal to result[1].
In the same manner, the third iteration will result in the first three array elements all pointing to (still the same) GlideElement object, which now contains the sys_id of the third record in the dataset.
Ultimately in the example given above, after eight iterations, the array will contain eight elements, all of which refer to the sys_id of the same (eighth) record.

Solution

Now that we can finally explain to Marvin Gaye what’s going on, the solution can’t be far. The trick is to store something else than the reference in the array. If we were to retrieve the sys_id’s string value from the GlideElement object, we would create a new string. This value would not change when calling next() on the GlideRecord, because the grUser object has no knowledge of the new string’s existence. So instead of

result.push(grUser.sys_id);

push the string-converted value on the array. Use whichever of the three options has your preference:

result.push(”+grUser.sys_id);
result.push(grUser.sys_id.toString());
result.push(grUser.getValue(‘sys_id’));

Change the line in the script and run it again; the script will now print eight different user id’s.

I hope this was useful info! Please drop a comment if you would like more information!
.img[at].img

One Response to “The nuts and bolts of JavaScript”

  1. Venkat Says:

    Nice one..

Leave a Reply