Search code examples
javascriptnode.jses6-promise

JS Promises wait for the function and get the returned object


I'm trying to build some test helper functions that will help me to run some hooks with Mocha to test my GraphQL queries(just that you understand the context). I want to do the following steps each time before I run the tests:

  1. Connect to mongoDB with mongoose
  2. Start the server (node)
  3. Add some test data directly into the database (via mongoose models)

// this function returns back the server instance because I need it in the 'after' hook to be able to stop it after the tests execution.

export const startServer = () => {
  mongoose.Promise = global.Promise;
  mongoose.connect(MONGO_URI_TEST);
  return mongoose.connection.once('open', () => {
    const app = express();
    app.post('/graphql', bodyParser.json(), graphqlExpress(req => {
      return { schema: executableSchema };
    })
    );
    return app.listen(9000, () => {
      console.log('Server started!');
    });
  });
};

// now here's the before hook where the server is started
let server;
before( done => {
  server = startServer(done); // here I need to wait 
  // some other async code that inserts test data into mongo
  const user = new User({email: [email protected]});
  user.save().then(() => {
    // saved successfully
    done();
  })
});

I'm quite new in the JS world, how do you manage to wait (without async await syntax) until all the promises from startServer function are resolved and you get back the server instance and just after start inserting data into the database? It's a dummy question but also some links with a better explanation of this concept that I have here would be appreciated.

LE: The entire question has reduced to the followings:

const createServer = () => {
const server = app.listen(port, () => {
  //callback body here
  });
};

How to convert the callback to a Promise and at the same time return a valid server reference such that in the end I can do this:

const someOtherFunction = () => {
  createServer().then(myValidServerInstance => {
    //do something with that instance
  }
}

Solution

  • TLDR: Some of the things about JS Promises were not clear for me, like for example the returning of the result via resolve() method.

    So the right answer looks like this:

    export const startServer = () => {
      mongoose.Promise = global.Promise;
    // first mistake was the use of callbacks rather than Promise approach offered by mongoose.connect
      return mongoose.connect(MONGO_URI_TEST).then(() => {
        console.log('Connected to MongoLab TEST instance.');
        return createServer();
      },
      err => {
        console.log('Error connecting to MongoLab TEST instance:', err);
      });
    };
    

    The second one was returning directly the result of listen() before the operation get finished. So I've moved the code that is starting the server in another method and wrap the result of listen() into a promise and resolve the promise only when the server actually started listening.

    const createServer = () => {
      const app = express();
      app.post('/graphql', bodyParser.json(), graphqlExpress(req => {
        return {
          schema: executableSchema,
          context: { headers: req.headers},
        };
      })
      );
      return new Promise((resolve, reject) => {
        const server = app.listen(9000, () => {
          if (server) {
            console.log('Server started on port 9000');
            resolve(server);
          } else {
            reject();
          }
        });
      });
    };