Async Behavior in Javascript - javascript

The following function -
a = [1,2,3];
function f() {
for(let i=0; i < a.length; i++){
setTimeout(() => console.log(i),3000)
}
}
gives me output - 0,1,2 which is as expected.
However, the following function -
function f() {
for(var i=0; i < a.length; i++){
setTimeout((function(el){console.log(el)})(i),3000)
}
}
gives the following output -
0 and throws following error
Uncaught TypeError: Cannot read property '__wrapper_2__' of undefined
However, in the last function if I remove setTimeout, I am getting expected behavior i.e. -
function f() {
for(let i=0; i < a.length; i++){ (function(i){console.log(i)})(i);
}
}
I am getting the expected output - 0,1,2
So, Why setTimeout is giving me error with when defining new function, but not with arrow functions ?

You're attempting to call the function in the argument list. It doesn't work for the same reason this doesn't:
function f() {
for (let i = 0; i < a.length; i++) {
setTimeout(console.log(i), 3000);
}
}
f();
The first argument of setTimeout is an uncalled function, like:
setTimeout(() => console.log(i), 1000);
or
const method = () => console.log(i);
setTimeout(method, 1000)
Edit: Also,
function f() {
for (var i = 0; i < a.length; i++) {
setTimeout(() => {
(function (el) {
console.log(el);
})(i);
}, 3000);
}
}
let a = [1, 2, 3];
f();
Will return 3, 3, 3 because after 3000ms the given function will be called with the global variable i as a parameter, and at that point i === 3;
However, in the following example, the value of i at the time is saved in the function. I believe that has something to do with closures, maybe someone else can follow up with a better explanation of that.
function f() {
for (var i = 0; i < a.length; i++) {
setTimeout(() => {
console.log(i);
}, 3000);
}
}
let a = [1, 2, 3];
f();

Related

Not being able to reference a variable from outside the loop

Iam not able to reference a variable from outside the loop in the following function. I want to use the value inside the function itself but the scope doesnt seem to allow it.
function coinOnTheTable(m, k, board) {
for (var i = 0; i < board.length; i++) {
for (var j = 0; j < m; j++) {
if (board[i][j] === "*") {
const a = `${i}${j}`;
return a;
}
}
}
}
Works, While
function coinOnTheTable(m, k, board) {
for (var i = 0; i < board.length; i++) {
for (var j = 0; j < m; j++) {
if (board[i][j] === "*") {
const a = `${i}${j}`;
}
}
}
return a;
}
gives an error
return a;
^
ReferenceError: a is not defined
That's because a variable defined as const or let has "block scope". That means it is only visible inside the nearest set of curly braces. So:
function foo() {
const foo = 'foo-string';
if(1) {
const bar = 'bar-string';
console.log(bar); // This is fine
}
console.log(foo); // This is also fine
console.log(bar); // Error!
}
To solve your problem, you need to define your variable outside of the block defined by the nested for loop. So:
function coinOnTheTable(m, k, board) {
let a;
for(var i = 0; i < board.length; i++){
for(var j = 0; j < m; j++){
if(board[i][j] === "*"){
a = `${i}${j}`;
}
}
}
return a;
}
Note, the reason it changes from const to let is because const variables cannot be reassigned, but since you want to declare it as undefined (at the start of the function, then assign a string to it (inside the loop), you need to be able to reassign it.
make a function scope:
function coinOnTheTable(m, k, board) {
let a;
for(var i=0;i<board.length;i++){
for(var j=0;j<m;j++){
if(board[i][j]==="*"){
const a=`${i}${j}`;
}
}
}
return a;
}

Javascript Closure is not working?

I know this seems silly. But just now I tried to test the closure in JS.
var funcList = [];
for(let i = 0; i < 3; ++i) {
funcList.push(() => { console.log(i); });
}
console.log('testing for reference');
funcList.forEach(func => func());
funcList.length = 0;
for(let i = 0; i < 3; ++i) {
funcList.push(
((i) => {
return () => { console.log(i); }
})()
);
}
console.log('testing for no reference');
funcList.forEach(func => func());
But unluckily, I encountered issues as follows:
The expected output should be (at least I thought ~~, highly possible fact is I'm wrong):
testing for reference
3
3
3
testing for no reference
0
1
2
But it gave me
testing for reference
0
1
2
testing for no reference
undefined
undefined
undefined
What is happening out here? Can anyone shed the light upon me?
Thank you so much for any possible helpful advice.
UPDATE 2018-3-24 Thanks for #Patrick Evans It's working as follows now:
var funcList = [];
for(var i = 0; i < 3; ++i) { // using var instead of let
funcList.push(() => { console.log(i); });
}
console.log('testing for reference');
funcList.forEach(func => func());
funcList.length = 0;
for(var i = 0; i < 3; ++i) {
funcList.push(
((i) => {
return () => { console.log(i); }
})(i) // pass in the `i`
);
}
console.log('testing for no reference');
funcList.forEach(func => func());
Updated 2018-03-30
A real closure should be
var funcList = [];
(function () {
let i = 0;
while (i++ < 3) {
funcList.push(() => { console.log(i); });
}
})();
console.log('testing for reference');
funcList.forEach(func => func());
You forgot to pass i's value to the function, which is expecting a parameter. Since parameter doesn't have a value it logs undefined. Try this:
funcList.push(
((i) => {
return () => { console.log(i); }
})(i) //Pass i's value as param
);

Confused about function closures

Can someone please explain why does it console log out 10, 10, 10 instead of 9, 9, 9?
When it goes through for loop shouldn't it stop at 9?
var foo = [];
for (var i = 0; i < 10; i++) {
foo[i] = function() {
return i;
};
};
console.log(foo[0]());
console.log(foo[1]());
console.log(foo[2]());
Whenever any function which is using any variable from parent scope is executed, it gets that value of a variable which it is holding at the time of function execution. In your case i is already reached to 10 at the time of execution because of i++.
For getting expected result, you can add IIFE to it, which will hold the value of i in its scope.
var foo = [];
for (var i = 0; i < 10; i++) {
foo[i] = (function(i) {
return function() {
return i;
};
})(i);
};
console.log(foo[0]());
console.log(foo[1]());
console.log(foo[2]());
You can use this syntax which keeps the context
for (let i = 0; i < 10; i++) {
foo[i] = () => i;
};
console.log(foo[0]());
console.log(foo[1]());
console.log(foo[2]());
https://jsfiddle.net/bandpay/8zat3bnn/

How does Generator.prototype.throw() work? Is an implicit next() included?

I am learning about Generator.prototype.throw(), and I test the code below.
var generator = function* () {
for(let i = 0; i < 10; i++) {
try {
var value = yield i;
console.log(i);
} catch(e) {
console.log("catch exception...");
}
}
};
var g = generator();
g.throw();
In this case, an error will be thrown saying "Uncaught undefined..." under chrome v49.
However, if we invoke g.next() firstly, then invoke g.throw(), it works.
var generator = function* () {
for(let i = 0; i < 10; i++) {
try {
var value = yield i;
console.log(i);
} catch(e) {
console.log("catch exception...");
}
}
};
var g = generator();
g.next(); // Object {value: 0, done: false}
g.throw(); // catch exception...
g.next();
// 1
// Object {value: 2, done: false}
In the meantime, the i value has been skipped as if g.next() was also executed.
How does this happen?
Both next() and throw() continue where you left. In your first example this would be before your first line:
-> throw() for(let i = 0; i < 10; i++) {
Therefore the error is not caught. In your second example this would be after the yield:
var value = yield i; -> throw()
Since the error gets caught, the application continues to the same point. After calling next() the application resumes from there.

Typescript get i value on a callback function when created

How can obtain the current value of i ?
var array = [1, 2, 3];
for (var i = 0; i < array.length; i++)
{
$(element).load("http://www.google.de", () => {
console.log(i);
}
}
Will return 2, 2, 2... How can i return 0, 1, 2 ?
It is because you are using a closure variable i from the outer function inside the ajax callback. Any changes made in the variable value will get reflected in the callback method.
One possible way is to use an anonymous function to provide a custom closure as given below
for (var i = 0; i < array.length; i++)
{
(function(i){
$(element).load("http://www.google.de", () => {
console.log(i);
}
})(i)
}

Categories