0

I have an array (targets) in global scope, the values of which I am passing to an external function [third party library, externalConverter] that does some text conversion. The values of this array are being passed in to the convert function and the conversion is happening fine.

const targets = [‘box’, ’box1’, ’box2’, ’box3’]

for (var i = 0; i < targets.length; ++i) {
    console.log(targets[i]); // this is coming out fine
    externalConverter
        .convert(data.text, targets[I])
        .then(results => {
            data.convertedText.push({
                [targets[i]]: results[0]
            });

            //above convertedText array comes out as
            //{“undefined: ”, “nice converted text”}, ...
        })
}

Inside the result of the Promise, I am trying to access the targets values but getting undefined values inside the function above. I am not sure why targets is suddenly becoming undefined

Any ideas?

zero298
  • 25,467
  • 10
  • 75
  • 100
Cipher
  • 5,894
  • 22
  • 76
  • 112
  • 1
    Possible duplicate of [How do I return the response from an asynchronous call?](https://stackoverflow.com/questions/14220321/how-do-i-return-the-response-from-an-asynchronous-call) – user5664615 Feb 08 '18 at 21:53
  • 1
    Possible duplicate of [JavaScript closure inside loops – simple practical example](https://stackoverflow.com/questions/750486/javascript-closure-inside-loops-simple-practical-example) – Patrick Roberts Feb 08 '18 at 22:00
  • When are you accessing targets? In the `.then()` or somewhere else? – zero298 Feb 08 '18 at 22:00

1 Answers1

1

The value of i will have progressed to its final value (i.e. targets.length) before any of the then callbacks have executed, and so any use of i as index will be out of range.

Use let i instead of var i to make separate instances of i that will not have this problem.

trincot
  • 317,000
  • 35
  • 244
  • 286
  • Perfect. Not super familair with Nodejs syntax but this works. A possible link to help delve more into this would be appreciated. – Cipher Feb 08 '18 at 21:56
  • `to make separate instances` -- while this is technically true if you dig into the specification, it would be much clearer to a larger audience to refer to this behavior as "lexical scoping". – Patrick Roberts Feb 08 '18 at 21:57
  • @PatrickRoberts, although `let` indeed has lexical (and thus block) scope, that aspect does not not convey the notion that the loop will *not* use a single version of that variable (with block scope) that is being incremented. – trincot Feb 08 '18 at 22:02
  • @Cipher, there are many articles discussing this. For instance [this](http://wesbos.com/for-of-es6/) – trincot Feb 08 '18 at 22:03
  • @trincot [I'm aware of how `let` works in a `for` loop](https://stackoverflow.com/a/37793317/1541563). And yes, "lexical scoping" does include this behavior. – Patrick Roberts Feb 08 '18 at 22:09
  • Just to add that this behaviour (new instances / bindings) is quite specific to the `for` loop. It is defined in the [specs](http://www.ecma-international.org/ecma-262/6.0/#sec-for-statement-runtime-semantics-labelledevaluation). When you think of it, it is quite advanced that `i++` reads the value from the current binding and the new binding gets the new value. Lexical scoping alone does not convey these mechanics. – trincot Feb 08 '18 at 22:15