tlnagy
tlnagy

Reputation: 3474

Anonymous function works as callback but defined function does not

I have a module that looks like this:

module.exports = AtomMarkdownLabels =
  # other stuff here

  file_changed_added: (file_path) =>
    fs.readFile file_path, 'utf-8', @process_yaml
    console.log 'file changed'

  process_yaml: (err, data) =>
    console.log "process_yaml is called"

I know file_changed_added is being called from some other function and I'm seeing the "file changed" output in the console, but process_yaml isn't if I change file_changed_added to

file_changed_added: (file_path) =>
    fs.readFile file_path, 'utf-8', (err, data) =>
      console.log "test"
    console.log 'file changed'

I see both "test" and "file changed" being called properly. What could be going on?

Upvotes: 0

Views: 33

Answers (1)

mu is too short
mu is too short

Reputation: 434616

=> has two slightly different purposes:

  1. When defining a named function (f = => ...) or anonymous function f(x, => ...)), => simply ensures that @ inside the function is that same as @ in the surrounding context.

  2. When defining a method in a class:

    class C
      m: => ...
    

    => ensures that @ inside m will be the instance of C.

Both uses are creating a bound function but they're binding to different things.

You're using this structure:

obj =
  func: =>
    # ...

That's equivalent to this:

f = =>
  # ...
obj =
  func: f

because you're using a plain old object rather than a class. So what is @ outside your AtomMarkdownLabels definition? @ won't be anything useful and in particular, it won't be your AtomMarkdownLabels object and it won't have a process_yaml property so @process_yaml inside file_changed_added is going to be undefined or an error.

I'm not sure what specifically Atom wants you to return but a class should work, something like this:

# Use a class so that => does what you're expecting it to do
class AtomMarkdownLabels
  # other stuff here

  file_changed_added: (file_path) =>
    fs.readFile file_path, 'utf-8', @process_yaml
    console.log 'file changed'

  # you may or may not need => here
  process_yaml: (err, data) =>
    console.log "process_yaml is called"

# Export an instance of your class
module.exports = new AtomMarkdownLabels

If you want to or must use a plain object then you could bypass @ completely and do it like this:

# Just plain old functions so define them that way
process_yaml = (err, data) ->
  console.log "process_yaml is called"

file_changed_added = (file_path) ->
  fs.readFile file_path, 'utf-8', process_yaml
  console.log 'file changed'

module.exports = AtomMarkdownLabels =
  # other stuff here

  file_changed_added: file_changed_added

or like this:

# Explicitly name your object rather than using @
module.exports = AtomMarkdownLabels =
  # other stuff here

  file_changed_added: (file_path) ->
    fs.readFile file_path, 'utf-8', AtomMarkdownLabels.process_yaml
    console.log 'file changed'

  process_yaml: (err, data) ->
    console.log "process_yaml is called"

This should solve your other CoffeeScript problem from a few days ago too.

Upvotes: 2

Related Questions