0

I have the below code where I am trying to recursively console.log the counter with a setTimeout. I was working on two versions, but I am getting errors for each.

In the first one I am not sure why setTimeout does not see this.timeTest as a function.

In second I understand that this.timeTest is outside of scope. Is there a way to get it into scope?

Server

const express = require('express')
const app = express()
const port = 3000
const time = require('./timeoutFun.js')

time.timeTest()

app.get('/', (req, res) => res.send('Hello World!'))

app.listen(port, () => console.log(`Example app listening on port ${port}!`))

Attempt 1

const time = {
    counter: 0,
    timeTest: function() {
        this.counter++
        console.log(this.counter)
        if (this.counter < 5) {
            this.timeTest()
        } else if (this.counter === 10) {
            console.log("done")
        } else {
            setTimeout(this.timeTest(), 3000);
        }
    }
}

module.exports = time;

Attempt 2

const time = {
    counter: 0,
    timeTest: function() {
        this.counter++
        if (this.counter < 5) {
            console.log("start")
            console.log(this.counter)
            this.timeTest()
        } else if (this.counter === 10) {
            console.log("done")
        } else {
            setTimeout(function () {
                console.log("wait")
                console.log(this.counter)
                this.timeTest()
            }, 3000);
        }
    }
}

module.exports = time;
lukechambers91
  • 691
  • 1
  • 6
  • 21
  • https://stackoverflow.com/questions/7890685/referencing-this-inside-setinterval-settimeout-within-object-prototype-methods – Andrew Li Dec 06 '18 at 16:59

2 Answers2

1

In the first example, you're passing the result of calling the timeTest function, rather than the function itself, i.e.:

setTimeout(this.timeTest(), 3000); // wrong
setTimeout(this.timeTest, 3000);   // right

As for the second example, this.timeTest is indeed out of scope because introducing the anonymous function in the setTimeout invocation creates a new "context", by which I mean a new value for this. In javascript, if you want to replace the default context assigned to the anonymous function, you can use the context binding function bind.

That's a little complex for newbies to javascript, and there's no need to do that here - just fix the first example as above and it will work as intended.

But for posterity, this is how you'd fix the second example:

setTimeout(function () {
    console.log("wait")
    console.log(this.counter)
    this.timeTest()
}.bind(this), 3000); // note the bind invocation here

You can also use what's known as arrow functions which, instead of assigning the function body a new context, assigns it the context of the enclosing scope (note this is only available in ES6).

setTimeout(() => { // no need for manual binding
    console.log("wait")
    console.log(this.counter)
    this.timeTest()
}, 3000); 
jonny
  • 3,022
  • 1
  • 17
  • 30
1

Change this:

setTimeout(this.timeTest(), 3000)

to this:

setTimeout(this.timeTest, 3000)

setTimeout takes a Function as it's first argument. You are passing the result of that function as the 1st argument, instead of the Function itself.

nicholaswmin
  • 21,686
  • 15
  • 91
  • 167