3

the following code works:

Experimenters = function(db)
{
    Object.apply(this, arguments);
    this.db = db;
};
util.inherits(Experimenters, Object);


Experimenters.prototype.getAll = function(req, res)
{    
    return this.db.Experimenter.getAllExperimentersWithExperiments()
    .then(function(exptrs) {
        res.json(200, exptrs);
    })
    .catch(function(error) {
        res.json(500, error);
    });
};

however the code for each of my request handlers is pretty much all the same, so I thought I'd reduce duplicate code by creating a generic request handler:

Experimenters.prototype.handleRequest = function(promise, res)
{
    return promise
    .then(function(success){
        res.json(200, success);
    })
    .catch(function(error) {
        if (error instanceof dbErrors.NotFoundError) {
            res.json(404, error);
        } else if ((error instanceof dbErrors.ValidationError) ||
                   (error instanceof dbErrors.UniqueConstraintError)) {
            res.json(422, error);
        } else {
            // unknown error
            console.error(error);
            res.json(500, error);
        }
    });
};

And modify my request handlers like this:

Experimenters.prototype.getAll = function(req, res)
{
    this.handleRequest(
        this.db.Experimenter.getAllExperimentersWithExperiments(),
        res);
};

But I'm getting:

TypeError: Object #<Object> has no method 'handleRequest' 

the code is called via my routes/index.js

// edited for brevity
var Experimenters = require("../controllers/experimenters");

module.exports.initialize = function(app)
{
    var exptrs = new Experimenters(app.db);

    app.get("/api/experimenters", exptrs.getAll);
};

which is called from my app.js:

 //edited for brevity
 var config = require(path.join(__dirname, "..", "config")),
     createDB = require(path.join(__dirname, "models")),
     routes = require(path.join(__dirname, "routes"));

 var db = createDB(config);
 app.set("db", db);
 // edited for brevity
 routes.initialize(app);
ckot
  • 819
  • 2
  • 10
  • 23
  • 5
    The value of `this` inside the `getAll()` function depends on how the `getAll()` function is called, not how it's defined, and the code that calls the function seems to be missing in the above code ? – adeneo Jun 29 '14 at 18:11
  • 1
    Likely related to https://stackoverflow.com/questions/20279484/how-to-access-the-correct-this-context-inside-a-callback – Felix Kling Jun 29 '14 at 18:15
  • In the line 3,on the first script,I think nothing Will be run after the return statement. – Lucas T. Jun 29 '14 at 18:35
  • @FelixKling thanks. yeat it had to do with bind() – ckot Jun 29 '14 at 20:09

2 Answers2

2

Updates:

you are getting this error because you should be binding exptrs to the function like this:

app.get("/api/experimenters", exptrs.getAll.bind(exptrs));

This is because you are passing the function exptrs.getAll as a parameter into the .get function which is called by app and therefore this in exptrs.getAll will be referring to app.

So another solution is to pass an anonymous function to get:

app.get("/api/experimenters", function(){exptrs.getAll()});

Normally when you get errors like Object #<Object> has no method 'handleRequest', it either means

  1. .prototype.handleRequest() is not defined properly, or

  2. the object that is calling .handleRequest() is not actually the correct object.

I believe in the return of .handleRequest it should be promise().then(...).catch(...), instead of promise.then(...).catch(...), because just having promise without () you are not calling the function.

Similar to

var b = function(){
 return 1   
};

function a(c){
    return c
}

var d = a(b);

console.log(d);

//it will not log 1, unless 
//function a(c){
//  return c()
//}

And in .getAll you should be returning this.handleRequest(..) too, rather than just calling it.

Archy Will He 何魏奇
  • 9,589
  • 4
  • 34
  • 50
  • interesting, so with exptrs.getAll.bind(this) 'this' refers to exptrs? – ckot Jun 29 '14 at 19:13
  • @ckot Sorry I made a careless mistake. The `this` in `exptrs.getAll.bind(this)` is referring to the `this` of `module.exports.initialize` and so doing `exptrs.getAll.bind(this)` will make `this` in `exptrs.getAll` refer to the `module.exports`. What you need to do is to bind(exptrs) so that `exptrs` will be the `this` in `exptrs.getAll`. – Archy Will He 何魏奇 Jun 29 '14 at 19:29
  • it was working anyway, but I added exptrs.getAll.bind(exptrs) and it didn't break anything. Probably wasn't necessary for this simple request handler, but doing this to all of my handlers, which might call other instance methods, should be ensured to work, right? – ckot Jun 29 '14 at 19:30
  • 1
    @ckot Yeah, unless you want `this` in the function to refer to something else (in this case `app`). To learn more about `bind` you can check out this [MDN article](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind). – Archy Will He 何魏奇 Jun 29 '14 at 19:35
0

I was able to solve this the following way:

First, I noticed that my handleRequest() made no use of 'this', so it could be a simple function

Secondly, I modified handleRequest() by adding a Promise.try() around the promise

handleRequest = function(promise, res)
{
    return Promise.try(function(){
        return promise;
    })
    .then(function(success){
        res.json(200, success);
    })
    .catch(function(error) {
        if (error instanceof dbErrors.NotFoundError) {
            res.json(404, error);
        } else if ((error instanceof dbErrors.ValidationError) ||
                   (error instanceof dbErrors.UniqueConstraintError)) {
            res.json(422, error);
        } else {
            // unknown error
            console.error(error);
            res.json(500, error);
        }
    });
};
ckot
  • 819
  • 2
  • 10
  • 23
  • 1
    Yup that is because you are passing the function `exptrs.getAll` as a parameter into the `.get` function which is called by `app` so and `this` in `exptrs.getAll` will be referring to `app`. – Archy Will He 何魏奇 Jun 29 '14 at 19:14