Scope of variables in callback function


#1

Hi,

I’m developing a package for sending code to a Terminal window on the mac. My package communicates to Terminal.app via Applescript. Applescript is called using the node-osascript module. At this point I can successfully launch Terminal and open a new window. Here is my current code:

module.exports =
  WinId: {}
  TabId: {}
  Osascript: {}

  # Starts a new terminal window and launches the REPL
  launchRepl: ->
    @Osascript = require 'node-osascript'
    scriptPath = __dirname + '/applescript/launchTerminal.applescript'
    # TODO: Make this file type agnostic
    @Osascript.executeFile scriptPath,
      { language : 'julia' }, (err, res, raw) ->
        if err
          console.error(err)
        else
          @WinId = res['win']
          @TabId = res['tab']

          console.info('Opened new terminal window with window ID ' +
                        @WinId + ' and tab ID ' + @TabId + '.')

Obviously, I would like to keep a reference to the Terminal window, so I can send any new bits of code to that same window. My Applescript therefore returns the window id and the tab id in a named list. node-osascript parses the list into an object, which is available in res. As you can see, I try to write the window and tab IDs to the variables WinId and TabId. Here is my problem: WinId and TabId are out of scope in the callback function of @Osascript.executeFile. Hence, the callback writes the values to local variables.

What would be the best way of storing the window and tab IDs for future reference in Atom? I’m sure this must be a general question for which some best practices answer exists. I’ve tried to find examples for this in other packages, but haven’t had any luck so far.

Thanks a lot!

Felix


#2

In coffeescript you can use fat arrows (=>) to write functions that use the context in which they were created:

someObject =
  someVar: 'someValue'
  someMethod: ->
    do -> # executes in global context
      console.log @someVar # logs undefined, unless global.someVar exists
    do => # executes in same context
      console.log @someVar # logs 'someValue'
someObject.someMethod()

So in your case, replacing the -> in @Osascript.executeFile with a => should do the trick.

Here is the documentation on bound functions.


#3

Sweet! Thank you so much. That did the trick! I’m new to coffeescript. Hope I learn these things quickly.


#4

One trick about the fat arrows (=>) that recently confused me is that at runtime this isn’t bound like you would expect. If you type this into the debugger console you’ll likely get the top level window context, not the object that you think the function should be bound to because of your use of the fat arrow.

The reason for this is that when Coffeescript is translated to Javascript all CoffeeScript this references are translated to a local _this variable. So at runtime it’s _this that is bound to because of the fat arrow, not this.