H
H
HoHsi2016-01-28 09:56:05
JavaScript
HoHsi, 2016-01-28 09:56:05

Why should an iterator be passed to a closure in a loop?

Good afternoon!
It's not the first day I write in JS, but I still can't figure out what's wrong with for in JS. Let's say:

'use strict';

const arrOfItems = [
    'a',
    'b',
    'c'
];

let arr = [];

let item;
for (let i = 0, len = 3; i < len; i++) {
    item = arrOfItems[i];

    arr.push(() => {
        return Promise.resolve(item); // Все промисы вернут C
    });
}

arr = arr.map(item => item());

Promise.all(arr)
.then((results) => {
    console.log(results);
});

but
'use strict';

const arrOfItems = [
    'a',
    'b',
    'c'
];

let arr = [];

let item;
for (let i = 0, len = 3; i < len; i++) {
    ((item) => {
        item = arrOfItems[i];

        arr.push(() => {
            return Promise.resolve(item);
        });
    })(item);
}

arr = arr.map(item => item());

Promise.all(arr)
.then((results) => {
    console.log(results);
});

or
'use strict';

const arrOfItems = [
    'a',
    'b',
    'c'
];

let arr = [];

for (let i = 0, len = 3; i < len; i++) {
    const item = arrOfItems[i];

    arr.push(() => {
        return Promise.resolve(item);
    });
}

arr = arr.map(item => item());

Promise.all(arr)
.then((results) => {
    console.log(results);
});

They return everything as it should, i.e. ['a', 'b', 'c']. I know what IIFE is and what lexical scoping is, but I don't understand:
1) These are all synchronous operations, ie. The JavaScript event engine must execute them in one iteration, but the for behaves like an asynchronous operation, i.e. first goes through all the options, and then pushes them.
2) Which of the JS operations are asynchronous?
3) When should closures be used besides loops?
4) Why, if you declare a variable in a loop, does it behave as if it is in a closure, because in fact it is not itself that is iterated, but i ?

Answer the question

In order to leave comments, you need to log in

2 answer(s)
B
bromzh, 2016-01-28
@bromzh

for-, if-, switch-blocks do not create a context.

arr.push(() => {
        return Promise.resolve(item); // Все промисы вернут C
    });

In the function, the variable i changes and after the loop becomes equal to its final value. Here is the closure and refers to this value. 775f8192fca64f7dba3d26d66086402c.png
And if you wrap it in iife, then at each iteration a local context is created in which a closure is created, which, in turn, will refer to the local variable item.
PS yes, in the second example it is not quite logically done, it is better to do it differently:
let item;
for (let i = 0, len = 3; i < len; i++) {
    ((item) => {
        item = arrOfItems[i];

        arr.push(() => {
            return Promise.resolve(item);
        });
    })(item);
}

a
for (let i = 0, len = 3; i < len; i++) {
    ((idx) => { // это для наглядности, её тоже можно назвать i и тогда она "затенит" внешнюю i
        let item = arrOfItems[idx];
        arr.push(() => {
            return Promise.resolve(item);
        });
    })(i);
}

Well, in the third example, const is used, and in es2015 it lives inside the block, so everything is ok there.
no, it's just that if you do not wrap it in iife, then all closures are associated with the same variable.
All I/O functions, well, and something else, such as timeouts, etc. for, if, etc - operators, they are not asynchronous in nature.
For encapsulation, for example. In general, closures are almost always used in one way or another.
See how to announce. If the old fashioned way, then its scope will be equal to the scope of the external function. If let/const, then their scope is limited to the block, i.e. closures in the loop will refer to different variables.

A
Alexey Zuev, 2016-01-28
@yurzui

In the first example, move the item declaration into a loop. Let and const have scope - block {}

for (let i = 0, len = 3; i < len; i++) {
    let item = arrOfItems[i];
    arr.push(() => {
        return Promise.resolve(item);
    });
}

The second example is the usual dance with a tambourine before the advent of ES6, but here you are doing it in a new way).
The third example is similar to my first
. You can also remember that lambda expressions preserve the context, and then
for (let i = 0, len = 3; i < len; i++) {
    arr.push(() => {
        return Promise.resolve(arrOfItems[i]); 
    });
}

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question