0

I'm trying to call a function inside an MySQL query, but everytime I call this function, I receive this: this.test is not a function

query: function(db) {
    db.query("SELECT * FROM test", function(err, result, fields) {
        if (err) throw err;
        for (var r = 0; r < result.length; r++) {
           this.test();
        }
    });
},
test: function(){
  console.log("test");
}

What do I need to change on my code?

Thank you.

Igor Soloydenko
  • 11,067
  • 11
  • 47
  • 90
F. Santos
  • 23
  • 4

2 Answers2

2

The modern way to address this in ES6 (which is available in node.js), is to use an arrow function definition for your callback to preserve the value of this.

query: function(db) {
    db.query("SELECT * FROM test", (err, result, fields) => {
        if (err) throw err;
        for (var r = 0; r < result.length; r++) {
           this.test();
        }
    });
},
test: function(){
  console.log("test");
}

By default, any function call resets the value of this based on how the function was called. So, when db.query() calls your callback, your original value of this is lost and not available inside the callback. Declaring a callback function like this with the ES6 arrow syntax specifically instructs the Javascript interpreter to keep the lexical value of this (the value your code has in this local scope) when executing the arrow function declared callback.


For completeness, there are several ways to work around this.

  1. Use an arrow function declaraing for the callback instead of a regular function declaration
  2. Save the value of this to a local variable that you can reference instead of this which is what Igor's answer shows.
  3. Using .bind(this) on the callback to force a value for this on the callback
  4. Create a bound version of this.test() that you can call anywhere that rebinds this with test so you can call that and not even use this inside your callback.

Before ES6, 2 and 3 were the common work-arounds. With ES6, the arrow function is generally the preferred way of doing this.


Also, please realize that:

if (err) throw err;

inside an async callback is not effective or useful error handling. To write good solid code, you need to write real error handling and decide what your app should actually do in case of this type of error. You will also probably find that using promises for all your async operations makes it easier to propagate async errors back to a place where it's easier to handle them.

jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • @FSantos - Since you accepted the `var that = this` answer, I wanted to make sure you saw these other options, including the ES6 arrow declaration which is really the more preferred option these days. – jfriend00 Oct 13 '17 at 22:14
1

You need to capture this in the closure. "that" pattern is commonly used for that purpose:

query: function(db) {
    var that = this;
    db.query("SELECT * FROM test", function(err, result, fields) {
        if (err) throw err;
        for (var r = 0; r < result.length; r++) {
           that.test();
        }
    });
},
test: function(){
  console.log("test");
}

As others correctly point out, you can use arrow function as well. Then no need in that variable:

query: function(db) {
    db.query("SELECT * FROM test", (err, result, fields) => {
        if (err) throw err;
        for (var r = 0; r < result.length; r++) {
           this.test();
        }
    });
},
Igor Soloydenko
  • 11,067
  • 11
  • 47
  • 90