THE PROBLEMS (SKIP TO BOTTOM FOR SOLUTION):
What you are trying to do is not easy to achieve with the code already present.
Let us first see why this does not work:
var arr = [1,2,3,4]
for(var i = 0; i<arr.length;i++){
    console.log("Before Timeout" + i);
    setTimeout(function(){console.log("Array val of arr["+i+"]="+arr[i]);},5000);
    console.log("After Timeout" + i);
}
console.log(i);
 
 
setTimeout sets an async operation, which will run minimum after 5000 ms. The JS execution does not stop for that. Read up about event loop and asynchronous operations to get more clarity.
The for loop itself will run in synchronous manner and all your callbacks inside setTimeout will be scheduled (but will run in future). The rest of the flow continues.
That is why all the Before and After logs are run first. And later the timeout code runs. When the callback code runs (after ~5 seconds) the value of i is already 4. That is why you are accessing arr[i] you actually access arr[4] which is undefined.
This is how var variables work. There will be lots of answers on SO to help you with scoping. But the gist is that all the above i point to the same instance i. This i exists in the outer scope as shown in the log.
Replacing var with let will sort this part out. let is block scoped and only exist between {}. Every iteration will have a different instance of i.
var arr = [1,2,3,4]
    for(let i = 0; i<arr.length;i++){
        console.log("Before Timeout" + i);
        setTimeout(function(){console.log("Array val of arr["+i+"]="+arr[i]);},5000);
        console.log("After Timeout" + i);
    }
console.log(i);
 
 
Notice how it is not accessible outside the loop.
But there is still the problem that your execution order is not as expected.
THE SOLUTION:
To actually solve your problem, there might be multiple approaches. I went with this one:
- Create a Promisewhich will actually run your asynchronoussetTimeoutcode.
- Use async await. Think ofawaitkeyword as something that stops execution unless the promise is resolved.
- Use an async immediately invoked function expression as await can not be used on top levels. Only inside functions which have asynckeyword in front of them.
- Keep the Beforelog outside the promise so it runs first and then later the other two logs run.
const prom = (func) => new Promise((resolve) => {
setTimeout(() => {
  func();
  resolve();
},5000);
});
(async () => 
{
var arr = [1,2,3,4]
for(var i = 0; i<arr.length;i++){
   console.log("Before Timeout " + i);
   let func = () => {
   console.log("Array val of arr["+i+"]="+arr[i]);
   console.log("After Timeout " + i);
   }
    await prom(func);
}
})();