how to pass by value into a javascript Promise

610 views javascript
10

I don't understand the behavior of this javascript program. My intent was to sequentially fetch three numbered resources, but it seems like "i" has been captured in a closure and only its final value is used.

function fetch(x) {
    console.log('fetching resource ' + x);
}

var prom = Promise.resolve();
for(var i=1; i<=3; i++) {
    prom = prom.then(() => { fetch(i);} );
}

//prints
//fetching resource 4
//fetching resource 4
//fetching resource 4

I don't know enough about js to solve this -- how might I modify this program to produce 1, 2, and 3? Do I need to use resolve() somewhere?

answered question

Just use let instead of var. var is function-scoped which means you only have one variable i that has the value 4 by the time your then callback is called. let is block-scoped so each iteration of the loop gets it's own variable i.

5 Answers

0

Create a const to store the value you want.

function fetch(x) {
    console.log('fetching resource ' + x);
}

var prom = Promise.resolve();
for(var i=1; i<=3; i++) {
    const j = i;
    prom = prom.then(() => { fetch(j);} );
}

posted this
4

Change var to let

function fetch(x) {
    console.log('fetching resource ' + x);
}

var prom = Promise.resolve();
for(let i=1; i<=3; i++) {
    prom = prom.then(() => { fetch(i);} );
}

Or create an IIFE for the same

function fetch(x) {
    console.log('fetching resource ' + x);
}

var prom = Promise.resolve();
for(var i=1; i<=3; i++) {
    prom = (function(i){ return prom.then(() => { fetch(i);} );})(i);
}

posted this
4

Wrap your for each content into closure to maintain the value of i;

function fetch(x) {
    console.log('fetching resource ' + x);
}

for(var i=1; i<=3; i++) {
     (function(index){Promise.resolve().then(() => { fetch(index);} )})(i);
}

posted this
2

I suggest you to put the promise inside the for loop, like this:

function fetch(x) {
    console.log('fetching resource ' + x);
}


for(var i=1; i<=3; i++) {
    var prom = Promise.resolve(i);
    prom.then(fetch);
}

Or if you want to define the promise outside the loop, you can create a temporary array to use Promise.all

function fetch(x) {
    console.log('fetching resource ' + x);
}

var arr = [];

for(var i=1; i<=3; i++) {
    arr.push(i);
}

Promise.all(arr).then(values =>
{
  values.forEach(fetch);
});

posted this
6

The issue here has almost nothing to do with Promises and everything to do with closures. Your code:

for(var i=1; i<=3; i++) {
    prom = prom.then(() => { fetch(i);} );
}

means "when the promise resolves, call that anonymous function, which calls fetch() with the current value of i." The current value. Since you have already exited the for loop at this point (since the anonymous function is guaranteed not be called until you return from this code), i is equal to 4.

Ricky Mo offered a practical way to do what you apparently want to do.

posted this

Have an answer?

JD

Please login first before posting an answer.