This question already has answers here:
Is Chrome’s JavaScript console lazy about evaluating objects?
(7 answers)
Closed 5 years ago.
I really don't understand what could be going on here. So I have a function which takes a 2d array and a string and iterates through the 2d array and checks if any subarrays contain the string. But somehow I can't iterate over this object/array and I'm really confused as to what it actually is. I've done lots of iteration in javascript. I've tried for-in, for-of (es6), C stlye(like below), forEach(callback), map... nothing works.
_makeOrUpdateCase = (arrayOfArrays, str) => {
console.log(arrayOfArrays); //returns the object/array shown in image below, expected
console.log(typeof(arrayOfArrays)); //object
console.log(Array.isArray(arrayOfArrays)); //true - huh? is this array or object??
for (var i = 0; i < arrayOfArrays.length; i++) {
console.log(arrayOfArrays[i]) //this does not work
console.log(i); //nothing is printed out, as if the loop is simply ignored
}
Here is the output I get.. you can see that the stuff I'm printing in the loop is not executed. I know javascript can be weird but c'mon what is going on here, I have no idea what to google. I've iterated over arrays and objects lots of times in this code.
tl;dr: Your loop is fine but the array is empty even if it appears in the console that it is not.
You are logging/accessing the array before it was populated. That's evident from the Array[0] in the output (indicating an array of length 0), even though it appears to have an element. This can happen if you have an asynchronous process, like Ajax, but are logging/accessing the array before the asynchronous process is done.
If you hover over the little i in the console, it will tell you that the value was just evaluated, i.e. it shows the value the variable has at the time you click the triangle, not at the time you log the value in code. By the time you click the triangle, the asynchronous process will likely have finished.
However, if you use console.log(JSON.stringify(arrayOfArrays)); instead you will see that the array is empty.
Here is simple example that demonstrates the issue (open the browser console and expand the array, works in Chrome at least):
// Open the browser console
var arr = [];
console.log(arr);
arr.push(1,2,3);
The only solution is to call _makeOrUpdateCase only after the asynchronous process is done. Since you are not showing how/when/where the function is called, this is all that can be said about the problem.
Related: Is Chrome's JavaScript console lazy about evaluating arrays?
Related
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Is Chrome's JavaScript console lazy about evaluating arrays?
Chrome's js console is showing an array with a deleted value before the value is deleted. Why?
jsFiddle that demonstrates this behavior.
var list=[];
list.push("one");
list.push("two");
list.push("three");
console.log(list); //["two", "three", undefined × 1]
$("#output").append(JSON.stringify(list));//["one","two","three"]
list.shift();
$("#output").append($("<br>"));
console.log(list); //["two", "three"]
$("#output").append(JSON.stringify(list));//["two","three"]
In Chrome console.log is "delayed"; in this case to the end of the Program I believe.
That is, in Chrome, console.log does not stringify the input object immediately but rather performs the stringification "some time later" (after which the object has been modified in this case) but before it actually displays the result,
console.log(JSON.stringify(list)) would show the expected result.
This was reported as a bug as far back as Chrome 5 (the bug-fix target is Mstone-22, so not Chrome 20/21?) and a fix has been added to the webkit base:
As of today, dumping an object (array) into console will result in objects' properties being read upon console object expansion (i.e. lazily). This means that dumping the same object while mutating it will be hard to debug using the console.
This change starts generating abbreviated previews for objects / arrays at the moment of their logging and passes this information along into the front-end. This only happens when the front-end is already opened, it only works for console.log(), not live console interaction.
I am learning to use Sets in JavaScript and noticed some odd behavior when going through the documentation.
let beverageSet = new Set();
beverageSet.add('Rum');
beverageSet.add('Tea');
beverageSet.add('OJ');
console.log(beverageSet); // returns Set [‘Rum’, ‘OJ’]
beverageSet.delete('Tea');
console.log(beverageSet); // returns Set [‘Rum’, ‘OJ’]
I know that hoisting pulls all variables to the top of the scope before executing code, but I have not seen anything showing that it would affect console.log()/methods called on arrays and Sets.
Is this an effect of hoisting or something else? How can I get around this and console.log() the full Set before deleting an item and then console.log() again?
The console.log implementation sometimes decides to get the value asynchronously, thereby showing results that might have mutations applied to them, that really occurred after the console.log statement.
To avoid this, take a copy in one or the other way, for instance like this:
console.log([...beverageSet]); // convert set to new array.
See also Bergi's answer to a question concerning the asynchronicity of console.log.
There is also a chromium issue on this: 50316 - Console.log gives incorrect output in Chrome Developer Tools console. It might be an interesting read.
When running my unit tests, sometimes all are OK, sometimes some random test(s) fail for one controller only for some reason I can't see.
The failures report something like:
Expected spy exec to have been called with [ Object({}) ] but actual calls were [ Object({}) ]
And I really can't find any difference between expected and actual calls.
(using compare tools)
What separates this controller from others is that it contains recursion.
It contains a data-source that returns an array and for every item in that array asynchronous code is executed, something like:
var array = [{id:1}, {id:2}, {id:3}];
//first check the entire array
//then process the entire array
//then do something after the entire array is processed.
checkArray(0, array, object).then(function(){
processArray(0, array, object).then(function() {
doSomething(object);
});
});
function checkArray(index, array, object) {
return $q(function(resolve) {
var record = array[index];
//object is altered in doSomeStuff
doSomeStuff(record, object).then(function(){
if(++index !== array.length) {
return resolve(checkArray(index, array, object));
} else {
return resolve(true);
}
});
});
});
The actual code may stop halfway checking or processing the array do to some function calling an error, in which case an error popup is shown and the objects final state is compared in the unit tests.
There are however also tests failing again within this same controller (for no reason?) that do not make use of these recursively executed functions.
These spies are however actually called with an object that at the time of calling is slightly different. Jasmine does however not keep the state of an object at calling time, but instead reports the final state of the object.
(that's fine, it's just not as executed, but I don't care)
The code as is functions as required, I would however also like for the tests to run consistently without imaginary errors.
How could I prevent these errors from popping up?
(I see no other things that are really different from other controllers, therefore I assume that this recursion is the cause for my troubles)
When I eliminate the recursion by setting the array size to 1, I however still get the inconsistent results.
My objects contained a new Date() object.
Because the difference in seconds was less then 0.5 seconds it did not show as a difference, on many occasions the difference in time was less than a millisecond, and the results of the expected object and actual object were therefore identical.
By replacing the Date object with some random string the test consistently succeeded as expected.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Is Chrome's JavaScript console lazy about evaluating arrays?
I have the following snippets in javascript whose output makes me feel that something is going wrong.
1.
a=2;
console.log(a);
a+=2;
console.log(a);
Output:2 4 ; as expected
2.
t=[0,2];
console.log(t);
t[0]+=2;
console.log(t);
Output: [2,2] [2,2]
Shouldn't the output be [0,2] [2,2] ? And whats the difference between the above two cases that results in the different answers in both the cases?
It's because the log is delayed until Chrome has time to do it (i.e. your scripts releases the CPU).
Try this to understand what happens :
var t=[0,2];
console.log(t);
setTimeout(function() {
t[0]+=2;
console.log(t);
}, 1000);
It outputs what you expect.
Is that a bug of Chrome ? Maybe a side effect of an optimization. At least it's a dangerous design...
Why is there a difference ? I suppose Chrome stores temporarily what it must log, as a primary (immutable) value in the first case, as a pointer to the array in the last case.
console.log in chrome/ff is asynchronous and objects that are logged are interpreted at the time when they're expanded. . Instead make a copy of the object if you want to see its value at that time (for an array):
t=[0,2];
console.log(t.slice(0));
t[0]+=2;
console.log(t);
With an array, calling .slice will duplicate the array and not create a reference.
I wouldn't suggest using a time out: this really doesn't solve the problem, just circumvents it temporarily.
Everything you're doing is right, but chrome's logging is screwy/delayed. Try making a copy of the variable and adding to that and you'll see that your code is correct.
Chrome's logging is delayed in the newer versions, no problem from your end. Make a copy of the variable or use setTimeout.
This bit of code I understand. We make a copy of A and call it C. When A is changed C stays the same
var A = 1;
var C = A;
console.log(C); // 1
A++;
console.log(C); // 1
But when A is an array we have a different situation. Not only will C change, but it changes before we even touch A
var A = [2, 1];
var C = A;
console.log(C); // [1, 2]
A.sort();
console.log(C); // [1, 2]
Can someone explain what happened in the second example?
Console.log() is passed a reference to the object, so the value in the Console changes as the object changes. To avoid that you can:
console.log(JSON.parse(JSON.stringify(c)))
MDN warns:
Please be warned that if you log objects in the latest versions of Chrome and Firefox what you get logged on the console is a reference to the object, which is not necessarily the 'value' of the object at the moment in time you call console.log(), but it is the value of the object at the moment you open the console.
Pointy's answer has good information, but it's not the correct answer for this question.
The behavior described by the OP is part of a bug that was first reported in March 2010, patched for Webkit in August 2012, but as of this writing is not yet integrated into Google Chrome. The behavior hinges upon whether or not the console debug window is open or closed at the time the object literal is passed to console.log().
Excerpts from the original bug report (https://bugs.webkit.org/show_bug.cgi?id=35801):
Description From mitch kramer 2010-03-05 11:37:45 PST
1) create an object literal with one or more properties
2) console.log that object but leave it closed (don't expand it in the console)
3) change one of the properties to a new value
now open that console.log and you'll see it has the new value for some reason, even though it's value was different at the time it was generated.
I should point out that if you open it, it will retain the correct value if that wasn't clear.
Response from a Chromium developer:
Comment #2 From Pavel Feldman 2010-03-09 06:33:36 PST
I don't think we are ever going to fix this one. We can't clone object upon dumping it into the console and we also can't listen to the object properties' changes in order to make it always actual.
We should make sure existing behavior is expected though.
Much complaining ensued and eventually it led to a bug fix.
Changelog notes from the patch implemented in August 2012 (http://trac.webkit.org/changeset/125174):
As of today, dumping an object (array) into console will result in objects' properties being
read upon console object expansion (i.e. lazily). This means that dumping the same object while
mutating it will be hard to debug using the console.
This change starts generating abbreviated previews for objects / arrays at the moment of their
logging and passes this information along into the front-end. This only happens when the front-end
is already opened, it only works for console.log(), not live console interaction.
The latest guidance from Mozilla as of February 2023:
Don't use console.log(obj), use console.log(JSON.parse(JSON.stringify(obj))).
This way you are sure you are seeing the value of obj at the moment you log it. Otherwise, many browsers provide a live view that constantly updates as values change. This may not be what you want.
Arrays are objects. Variables refer to objects. Thus an assignment in the second case copied the reference (an address) to the array from "A" into "C". After that, both variables refer to the same single object (the array).
Primitive values like numbers are completely copied from one variable to another in simple assignments like yours. The "A++;" statement assigns a new value to "A".
To say it another way: the value of a variable may be either a primitive value (a number, a boolean, null, or a string), or it may be a reference to an object. The case of string primitives is a little weird, because they're more like objects than primitive (scalar) values, but they're immutable so it's OK to pretend they're just like numbers.
EDIT: Keeping this answer just to preserve useful comments below.
#Esailija is actually right - console.log() will not necessarily log the value the variable had at the time you tried to log it. In your case, both calls to console.log() will log the value of C after sorting.
If you try and execute the code in question as 5 separate statements in the console, you will see the result you expected (first, [2, 1], then [1, 2]).
Though it's not going to work in every situation, I ended up using a "break point" to solve this problem:
mysterious = {property:'started'}
// prints the value set below later ?
console.log(mysterious)
// break, console above prints the first value, as god intended
throw new Error()
// later
mysterious = {property:'changed', extended:'prop'}
The issue is present in Safari as well. As others have pointed out in this and similar questions, the console is passed a reference to the object, it prints the value of the object at the time the console was opened. If you execute the code in the console directly for example, the values print as expected.
Instead of JSON stringifying, I prefer to spread arrays (e.g. in your case console.log([...C]);) and objects: the result is quite the same, but the code looks a bit cleaner. I have two VS code snippets to share.
"Print object value to console": {
"prefix": "clo",
"body": [
"console.log(\"Spread object: \", {...$0});"
],
"description": "Prints object value instead of reference to console, to avoid console.log async update"
},
"Print array value to console": {
"prefix": "cla",
"body": [
"console.log(\"Spread array: \", [...$0]);"
],
"description": "Prints array value instead of reference to console, to avoid console.log async update"
}
In order to get the same output as with console.log( JSON.parse(JSON.stringify(c))), you can leave out the string part if you wish. Incidentally, the spread syntax often saves time and code.