Search code examples
node.jstypescriptes6-promise

node.js: Having a weird issue with a Promse's then condition not getting called


I am working on the following Promise scenario and am not understanding why the second then condition is not getting hit in the setup method. When this code runs in app.js, I see where initialize() is getting hit and that it waits for the db connection to be made as expected but once the resolve condition is reached in initialize, nothing else appears to get called.

I have breakpoints set in the catch but that is never called; neither is the catch reached in the setup method. I would expect that the initializeAppSettings would be called immediately after this completes.

Where am I going wrong with this promise scenario?

setup = () => {
    let p = new Promise(initialize)
        .then(this.initializeAppSettings)
        .then(() => {
            this.appServer.listen(3000, () => {
                console.log('Server started on port 3000 :)');
            });
        })
        .catch((err: Error) => {
            throw err;
        });
}

export let initialize = (): Promise<Container> => {
    let p = new Promise((reject, resolve) => {
        
        var container = new Container();

        initializeDBConnection(process.env.DB_HOST)
            .then((dbClient: Repository.DbClient) => {
                container.bind<DbClient>(TYPES.DbClient).toConstantValue(dbClient);

                resolve(container);
            })
            .catch((err: Error) => {
                reject(err);
            });
        });

        return p;
    }


this.initializeAppSettings = (container: Container): Promise<any> => {
    /*
    * This code is never called
    */
    let p = new Promise((reject, resolve) => {
        let server = new InversifyExpressServer(container);
        server.setConfig((app) => {
            app.use(bodyParser.urlencoded({
                extended: true
            }));
            app.use(bodyParser.json());
            app.use(express.static(path.join(__dirname, "public")));

            //configure pug
            app.set("views", path.join(__dirname, "views"));
            app.set("view engine", "pug");

            //mount logger
            app.use(methodOverride());
        }).setErrorConfig((app) => {
            if (process.env.NODE_ENV === "development") {
                app.use(function (err: Error, req: express.Request, res: express.Response, next: express.NextFunction) {
                    else {
                        //res.status(500).send('An error occurred while processing your request.');
                        res.statusCode = 500;
                        return res.json({
                            errors: ['An error occurred while processing your request.']
                        });
                    }
            }
        });

        this.appServer = server.build();
        resolve();
    });

    return p;
}

Solution

  • The outer promise you create here:

    setup = () => {
        let p = new Promise(initialize)
            .then(this.initializeAppSettings)
            .then(() => {
                this.appServer.listen(3000, () => {
                    console.log('Server started on port 3000 :)');
                });
            })
            .catch((err: Error) => {
                throw err;
            });
    }
    

    is never resolved, thus the .then() handler attached to it is never called.


    You have a number of issues here. I don't think I can cover them all, but I will go through a few of them.

    First, you want to avoid the promise anti-pattern of creating a new promise to wrap another one. You simple don't need to do that. So, if initialize() was a function that returns a promise that resolves when it's done (which is another issue in your code, but lets assume it was written properly), then you can just do this:

    setup = () => {
        return initialize()
          .then(this.initializeAppSettings.bind(this))
          .then(() => {
                this.appServer.listen(3000, () => {
                console.log('Server started on port 3000 :)');
            });
        });
    }
    

    Now, because the completion this.appServer.listen() is not connected to your promise chain in any way, it will get started when you want it to, but it's completion will not be coordinated with your promise at all (it will just complete whenever it wants to). You could "promisify" it if you wanted it to be chained into your promise chain.

    Note also that I've changed this.initializeAppSettings to this.initializeAppSettings.bind(this) to preserve the value of this inside the .then() handler.

    Now, the above only works properly if initialize() returns a promise that is resolved at the appropriate time. You do attempt to do that, but again you're doing it with the promise anti-pattern and you are . Rather than create your own promise, you should instead return the one you already have like this:

    export let initialize = (): Promise<Container> => {
        var container = new Container();
        return initializeDBConnection(process.env.DB_HOST)
            .then((dbClient: Repository.DbClient) => {
                    container.bind<DbClient>(TYPES.DbClient).toConstantValue(dbClient);
                    // make resolved value be the container
                    return container;
        });
    }