0

I'm struggling with a block of code involving closures in a set of promises. This example illustrates the problem. The issue is getting the last of these three promise examples to have a closure on q and use the result of the promise. The first two work and it appears to be because the promises' success handlers are just the closure function without any other argument, meaning the result of the promise isn't explicitly passed as an argument. The success function of the third promise attempts to pass a property of the result object but that results in there not being a closure on q. The objective is to have q be 5 and not 7. I apologize because I am sure I've used all the wrong coding terms.

Could you please explain what I am missing? Thank you.

let q = 1;
let f = (function (x) {
  return function (r) {
    result(x, r);
  }
})(q);

q = 5;

promise_test().then((r) => {
  f(r);
}, () => { });

promise_test().then((function (x) {
  return function (r) {
    result(x, r);
  }
})(q), () => { });

promise_test_obj().then((o) => (function (x) {
  return function (r) {
    result(x, r);
  }
})(q)(o.m), () => { });

q = 7;

function promise_test() {
  return new Promise((resolve, reject) => {
    setTimeout(function () {
      resolve('promise test resolved');
    }, 100);
  });
}

function promise_test_obj() {
  return new Promise((resolve, reject) => {
    setTimeout(function () {
      resolve({ 'm': 'promise test_obj resolved' });
    }, 100);
  });
}

function result(a, r) {
  console.log('q : ' + a + ', r : ' + r);
}
Geuis
  • 41,122
  • 56
  • 157
  • 219
Gary
  • 2,393
  • 12
  • 31
  • 1
    wow, you should really clean up the format of your code. I'm hoping that was just an issue when posting here and not what your actual source looks like. – Adam H Feb 12 '20 at 21:01
  • 2
    @Gary using some better variable names than single letters would be more helpful in reading your code. As-is, its extremely difficult to parse. – Geuis Feb 12 '20 at 21:06
  • @Gary it's ok, just in the future simply paste what you have and click the `tidy` button. With @Geuis helping out with the formatting I'll try and decipher what's happening here but please take his advice too with variable and function names. – Adam H Feb 12 '20 at 21:09
  • Sorry man, I tried but I can't make sense of what this supposed to be doing and following the logic is, well, very difficult to say the least. If you can update your snippet with cleaner variable & function names I can take another crack. @ me if you make those updates and want me to take another look. – Adam H Feb 12 '20 at 21:12
  • I think the success function doesn't process until after the promise resolves, such that the closure set up within the promise doesn't take place until after the value of q is changed. In the first two promises, the closure is set before the promise resolves. – Gary Feb 12 '20 at 21:12
  • Gary, why did you delete this [other question](https://stackoverflow.com/questions/60290456/order-of-promise-array-in-promise-allsettled-and-order-in-which-database-transac/60290659?noredirect=1#60290456) we were working on? You asked a question. I answered it. You should accept the answer rather than delete your question. – jfriend00 Feb 19 '20 at 03:01
  • @jfriend00 I appreciate your attempt at answering my question. I undeleted the question and provided a more specific example that should better illustrate what I am asking. It has to do with the order of database transations and requests more than with promises. I deleted it because it appeared to be of little interest and I wasn't getting my point across. – Gary Feb 20 '20 at 05:14

2 Answers2

2

Omitting all that unnecessary IIFE chaos, you essentially have

q = 1,
f = (…)(q);

q = 5;
….then((…)(q));

….then(o => (…)(q)(o.m));
q = 7;

The difference is that in the third example, the q variable is evaluated inside that o => … arrow function, which is called asynchronously after the q = 7 assignment. In contrast, the other two evaluate q immediately, so it takes the values of 1 and 5 respectively.

To fix this, write

f = (function(x) {
  return o => result(x, o.m)
}(q); // here q is still 5
promise_test_obj().then(f);

See also closure not working.

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • You are a true gentleman for digging into this and helping Gary out, I tip my hat to you and send an upvote your way. – Adam H Feb 12 '20 at 21:18
1

The function passed to then method in the promise_test_obj example will be executed after 100ms when the value of q in the outer scope is set to 7.

  o =>
    (function(x) {
      return function(r) {
        result(x, r);
      };
    })(q)(o.m),

So at the time when this function is called with argument { m: "promise test_obj resolved" } the q value is 7. The following code from the above example:

(function(x) {
      return function(r) {
        result(x, r);
      };
    })(q)

is called asynchronously whereas in the other 2 examples it is called synchronously.

Tomasz
  • 657
  • 3
  • 9
  • Thank you. I appeciate it. I accepted the answer just before yours posted and both are about the same. Thanks again. – Gary Feb 12 '20 at 21:50