You don't even know that it will read the same value. In an asynchronous environment something else could have written to arr[0] between your reads.
while(true) {
if (arr[0] === null) {
runNextInstruction();
} else {
handleInterrupts(arr[0]);
}
}
This code never sets any bits in arr[0]. But if some other code sets some bits it will handle them and clear them. This doesn't work if the language caches previous reads of arr[0]. And that's why most languages don't cache this for you. No amount of analyzing this code will tell you how it should be cached. For that you have to understand the world around the code.
Now some languages, like Rust, will force you to be explicit about things like shared mutable state. Javascript isn't one of them. And even in Rust you never really know if your code is the only thing that knows the address you're looking at. Sometimes what's writing to that address is hardware. Sometimes it's cosmic rays.
When you cache it yourself you are changing the logical behavior of the program. It may give you the same logic 99.9999% of the time. But that's still not 100%. It's up to you whether to care about that.
As for the amount of time, most hardware has a memory hierarchy. Here data gets copied from large slow memory to small fast memory as it gets used. Miss that fast memory with your read and it has to go access slow memory. This goes from CPU registers all the way down to the page file on the hard drive.
Which kind of memory your read will hit changes over time. So no, it doesn't necessarily take the same amount of time. Even if you cache it yourself. You don't have that kind of control in a high level language.