Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Q.js, promises, classes and "this", what is the context?

I'm completely confused about the context inside a Q promise. I don't think that's Q specific, but general with all promises. What the hell is the context of this inside a class?

This code uses TypeScript, everything is static now, because I basically failed to do anything non-static. This code works fine.

I tried to add a private _config; instance variable and use the _getConfig method to set the _config in the constructor. But when I used this._config inside the method checkMongodbConnection, well, it wasn't the same object as what was returned by the _getConfig() method. (I watched variable states in debug mode)

So I guess that this, inside a class, because I call the code from a Q promise, don't have the class instance context.

I'm wondering if using promises is a good idea after all, if I run into context issues the code will just be a lot more difficult to understand and to debug. I would appreciate to understand why and made a choice in consequence. I don't want to lost the class instance context, that's way too much tricky. I'm afraid to use a technology that will actually makes things more complicated, I prefer callback hell to that.

///<reference path='./def/defLoader.d.ts'/>    
export class App {

    /**
     * Constructor.
     * Load the config.
     * @return {}
     */
    private static _getConfig(){
        if(typeof __config !== "undefined"){
            return __config;
        }else{
            require('./../../shared/lib/globals/services');
            return configHelper.load('_serverConfig', require('./../../shared/config/_serverConfig.json').path.config, __dirname + '/../../');
        }
    }

    /**
     * Check that the mongoose connection open correctly, meaning that the mongod process is running on the host.
     * @return {Q.Promise<T>|Function}
     */
    public static checkMongodbConnection(){
        var config = App._getConfig();

        // Building promise
        var deferred: any = Q.defer();

        if(config.game.checkMongodb){
            // Retrieves the mongoose configuration file, the env doesn't matter here.
            var mongodbConfig = require('./../../shared/config/mongodb.json')['development'];

            // Try mongoose connexion
            mongoose.connect('mongodb://' + mongodbConfig.host + '/' + mongodbConfig.database);

            // Bind connexion
            var db: mongoose.Connection = mongoose.connection;

            // Get errors
            db.on('error', function(err) {
                deferred.reject('Mongodb is not running, please run the mongod process: \n' + err)
            });

            // If the connexion seems to be open
            db.once('open', function callback () {
                // Close it
                db.db.close();

                // Resolve promise
                deferred.resolve();
            });
        }else{
            deferred.resolve();
        }

        // Get back promise
        return deferred.promise;
    }

    /**
     * Check that the redis connection is open, meaning that the redis-server process is running on the host.
     * @return {Q.Promise<T>|Function}
     */
    public static checkRedisConnection(){
        var config = App._getConfig();

        // Building promise
        var deferred: any = Q.defer();

        if(config.game.checkRedis) {
            // Create the redis client to test to connexion on server
            var redisClient:any = redis.createClient();

            // Get client errors
            redisClient.on("error", function (err) {
                deferred.reject(err);
            });

            // Try applying a key
            redisClient.set("keyTest", true);

            // Now key is applied, try getting it
            redisClient.get("keyTest", function (err, reply) {
                if (err) {
                    deferred.reject("Redis is not running, please make sure to run redis before to start the server. \n" + err);
                } else {
                    deferred.resolve();
                }
            });
        }else{
            deferred.resolve();
        }

        // Get back promise
        return deferred.promise;
    }
}

Code that calls the class:

Q.fcall(App.checkRedisConnection)
    .then(App.checkMongodbConnection)
    .then(function(result) {
         // run server
    }, console.error);
like image 910
Vadorequest Avatar asked Mar 20 '23 15:03

Vadorequest


1 Answers

The promises/A+ specification dictates explicitly that the value of this inside a promise chain is always undefined (strict mode) or the global object via:

2.2.5 onFulfilled and onRejected must be called as functions (i.e. with no this value).

Specified here.

If you're not using a promise library like Bluebird that allows setting this explicitly (via .bind), you can still utilize TypeScript's fat arrows (also in ES6) to call something with lexical this.

like image 155
Benjamin Gruenbaum Avatar answered Apr 02 '23 09:04

Benjamin Gruenbaum