1

I have the following code:

WaitModel = ->
  timesDelayed = 0
  maxDelay = 10

  return this

WaitModel.prototype =
  wait: ->
    console.log 'waiting'

    console.log this.timesDelayed, this.maxDelay

    if this.timesDelayed >= this.maxDelay
      console.log 'running'
      this.timesDelayed = 0

    else
      this.timesDelayed++
      setTimeout this.wait, 1000

    return

new WaitModel().wait()

I think this should produce output like:

waiting
0 10
waiting
1 10
...

But instead it produces output like:

waiting
0 10
waiting
undefined undefined

Where am I unsetting this.timesDelayed and this.maxDelay? Am I misunderstanding the way to make objects here?

1
  • the second time waiting is called, from setTimeout this.wait, 1000, the variable this no longer refers to that instance of WaitModel Commented Feb 3, 2012 at 19:08

4 Answers 4

5

jfriend00 has told you what you're doing wrong. The idiomatic way to solve this problem in CoffeeScript is to use the => (fat arrow) to define your wait method. I've also de-JavaScripted your CoffeeScript to make it more idiomatic:

class WaitModel
    constructor: (@timesDelayed = 0, @maxDelay = 10) ->
    wait: =>
        console.log 'waiting'
        console.log @timesDelayed, @maxDelay
        if @timesDelayed >= @maxDelay
            console.log 'running'
            @timesDelayed = 0
        else
            @timesDelayed++
            setTimeout @wait, 1000

new WaitModel().wait()

Demo: http://jsfiddle.net/ambiguous/aYFmL/1/

Sign up to request clarification or add additional context in comments.

4 Comments

You need the fat arrow to be on an anonymous function passed as the first argument to setTimeout. It shouldn't be needed (or wanted) on the wait: function, because its this value won't need any special binding.
@amnotiam: You can pass any sort of function to setTimeout, it doesn't need to be an anonymous one. It is fairly common to define CoffeeScript methods using => if the method is intended to be used as a callback, doing so avoids a bunch of pointless anonymous function wrappers.
@amnotiam That's subjective. If you know the function is going to be used in callbacks then putting it where mu has is fine. It also avoids you having to create many small anonymous functions whenever you wish to call the wait function.
@david, mu: Alright, I see that CS does the right thing and doesn't actually bind the this of the function on the prototype, but creates a bound function on each instance. So... +1 :)
2

setTimeout doesn't preserve the value of this. You can save this into a local variable called self before the call to setTimeout or anything you want to call it and reference it that way.

I don't know coffeescript, but in javascript, it would be like this:

  wait: function() {
    var self = this;
    if (this.timesDelayed >= this.maxDelay) {
      console.log('running');
      this.timesDelayed = 0;
    } else {
      this.timesDelayed++;
      setTimeout(function() {self.wait()}, 1000);
    }
  }

Comments

2

Looks like CS has auto this binding.

setTimeout => @wait(), 
1000

With the fat arrow, you can consider this in the anonymous function to be its outer value.

Comments

1

Edit: This explains it better than I did:
http://bonsaiden.github.com/JavaScript-Garden/#function.this

Your example gives this output on my machine:

waiting
undefined undefined
waiting
undefined undefined

First, constructors shouldn't return anything, so remove the return this line. The reason is because new creates the new object and the constructor merely populates it.

Inside the constructor, this is bound to the object you're creating and normal variables have local scope as usual. This means you have to say this.timeDelayed etc. in the constructor -- else your two variables will fall out of scope when the constructor exits and won't show up as fields or anything later.

With these changes, my output matches yours:

waiting
0 10
waiting
undefined undefined

So now onto your problem.

setTimeout queues a function to run. Functions are just functions -- they don't carry extra information about which object they apply to, even if you treat them as methods (like this.wait does). This plays badly with this because in Javascript, the this keyword asways has dynamic scope, which means the caller gets to pick what it means through something like

someobject.foo()

or whatever. Inside the call to foo, this is bound to someobject. If we took it out:

var foo = someobject.foo
foo()

inside foo, this would be undefined. To make it work, we'd have to say:

foo.call(someobject)

This is important because setTimeout doesn't set the value of this when the timeout function runs. Instead, replace your call:

setTimeout this.wait, 100

with something like:

  self = this
  setTimeout (-> self.wait()),  100

This works because the function no longer calls this, it calls self which is an ordinary variable.

Coffeescript includes a few tricks and shortcuts here. First, you can avoid capturing self in a closure by using the fat arrow syntax, which automatically binds this in the function to the current object no matter where it's referenced:

Second, you can use @ as a shortcut for this. to write instance variables/methods. Thus, my final code sample looks like:

WaitModel = ->
  @timesDelayed = 0
  @maxDelay = 10


WaitModel.prototype =
  wait: ->
    console.log 'waiting'

    console.log @timesDelayed, @maxDelay

    if @timesDelayed >= @maxDelay
      console.log 'running'
      @timesDelayed = 0

    else
      @timesDelayed++
      setTimeout (=> @wait()),  100

    return

new WaitModel().wait()

2 Comments

If you aren't using coffee script classes, you should not be replacing the prototype object, but extending it instead. WaitModel.prototype.wait = ->
Alex: that actually explains another problem I had. Thanks!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.