0

There there is a function that expects a callback function that receives a data argument. I need a way to access foo inside the callback. From reading I found that closure scopes can be the solution, however I was struggling to find an example where the callback is receiving a data object as well (or any variable for that matter).

 const foo = 1;


 this.editor.functionThatExpectsCallback('genericString', (data) => {
       // do stuff with foo here, e.g.
       const bar = foo + data.num;      

       return bar;
  })

Basically the issue is foo is undefined inside the callback scope, and I need it to be defined, while still maintaining access to data as well. I would really appreciate any help I can get here. If you need more information I'd be happy to supply it.

Jordan Pagni
  • 454
  • 3
  • 11
  • 1
    "*Basically the issue is foo is undefined inside the callback scope*" then your code doesn't demonstrate that because it *would* be defined with the code you have. – VLAZ Dec 01 '21 at 17:48
  • Well I simplified my code for the sake of this post, but I do not have access within. Are you sure this is always the case? – Jordan Pagni Dec 01 '21 at 17:49
  • [Yes, I am](https://stackoverflow.com/questions/500431/what-is-the-scope-of-variables-in-javascript) – VLAZ Dec 01 '21 at 17:50
  • I believe the callback function is being executed remotely in a different service. Could that be the cause of foo being undefined? – Jordan Pagni Dec 01 '21 at 17:52
  • If the function is serialised and then deserialised, then - yes, it loses all context and scope. – VLAZ Dec 01 '21 at 17:53
  • that must be it then, is there any way around this, or do I have to find a way to pass foo through data somehow? – Jordan Pagni Dec 01 '21 at 17:54
  • 1
    First you'd probably have to check the documentation of whatever you're calling - it might provide a mechanism for that. If not, then you would unfortunately have to generate the function from string like ``new Function("data", `const bar = ${foo} + data.num; return bar;`)`` but you have to be very careful you don't happen to produce invalid code. – VLAZ Dec 01 '21 at 17:57
  • yeah the documentation is lacking unfortunately. Thanks for the suggestion on how to solve this! – Jordan Pagni Dec 01 '21 at 18:09

1 Answers1

1

Your current code is correct because that is how scopes work - anything from an outer scope is visible within an inner one.

The only reason why it would not work is if the function was serialised and then deserialised - that completely removes all scope information:

{ //different scope here
  const foo = 1;
  const result = functionThatExpectsCallback('genericString', (data) => {
    // do stuff with foo here, e.g.
    console.log("foo is:", typeof foo);
    const bar = foo + data.num;

    return bar;
  });
  
  console.log(result);
}

//dummy implementation that serialises and deserialises a function to run it:

function functionThatExpectsCallback(arg, callback) {
  return runFromString(callback.toString());
}

function runFromString(strFunc) {
  const fn = eval(`(${strFunc})`);

  return fn({ num: 2 });
}

The first way to solve this is by checking the documentation of whatever library is used - it might provide a way to also send along extra arguments.

If that is not an option, then you would need to do it the dirty way and generate the function from string. You can use the Function constructor to do that

{ //different scope here
  const foo = 1;
  
  const generatedFunction = new Function("data", `
    // do stuff with foo here, e.g.
    const bar = ${foo} + data.num;

    return bar;
  `);
  
  const result = functionThatExpectsCallback('genericString', generatedFunction);
  
  console.log(result);
}

function functionThatExpectsCallback(arg, callback) {
  return runFromString(callback.toString());
}

function runFromString(strFunc) {
  const fn = eval(`(${strFunc})`);

  return fn({ num: 41 });
}

You have to be very careful because it is easy to introduce minor mistakes when generating a function from string and you can end up with invalid code. For example, if foo was a string foo = "hello" then the generated code would be const bar = hello + data.num; which is invalid because hello would be a variable which was never defined, instead of the string literal "hello".

VLAZ
  • 26,331
  • 9
  • 49
  • 67