Properly terminate NodeJS Async Await - javascript

I wrote the following code to connect to MongoDB wrapped in a Promise, but as soon as it is connected, my NodeJS code is still running / not exit properly. May I know what is the proper way to return or terminate it?
function connectDB() {
return new Promise (resolve => {
MongoClient.connect(mongoUrl, function( err, db ) {
_db = db;
resolve(db);
});
})
}
(async function crawl() {
const db = await connectDB();
console.log('You are connected');
})()

This isn't really related to async/await, it also happens without. As #Torazaburo stated in the comments, the db driver maintains an open connection to the database, and this open socket (along with its eventhandlers) prevents node from quitting. Closing the connection helps:
function connectDB() {
return new Promise (resolve => {
MongoClient.connect(mongoUrl, function(err, db) {
if (err) reject(err);
else resolve(db);
});
});
}
(async function crawl() {
const db = await connectDB();
console.log('You are connected');
db.close();
console.log('You are no longer connected. Bye!');
})()

A promise have 2 paramaters: resolve and reject.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
You need to change your promise declaration:
return new Promise ((resolve, reject) => {
// ...
}
After that, add return statement to your promise:
return resolve(db)
Also, i suggest you to add an error log to your promise:
if (err) return reject(err)
And catch this error.
Good luck!

Related

ExpressJS MySql Pool Connection Query Return

I am just starting in ExpressJS. So here console.log works, so if I make a call, I do see it in terminal result dataset printed. But, it doesn't return it as response. I do console.log and return but still get no data in response..
index.js
...
app.get("/", async (req, res) => {
let data = await queries();
res.json(data);
});
...
Also tried
...
app.get("/", (req, res) => {
res.json(queries());
});
...
queries.js
function main(sql) {
return pool.getConnection((err, connection) => {
if (err) throw err;
return connection.query(sql, (errt, rows) => {
connection.release(); // return the connection to pool
if (errt) throw err;
console.log(rows);
return rows;
});
});
}
function queries() {
let Q = "SELECT * FROM `items`";
return main(Q);
}
I believe the problem is in the function main() if you do see the result in the terminal. Your functions in the index.js file seems okay. The issue is probably due to the promise not being sent back from the main() function in queries.js file. You need to do 2 things -
Wrap the functionality of the main() in a Promise and return that promise. The callback function of the Promise object takes resolve(use this to return result) and reject(use this to return error).
As the main() will return a promise, you queries() function needs to be async, therefore needs to await the return from the main().
I have attached an Image to show how I implemented this for a DNS-lookup function. You will find some similarities.
// Notice the resolve and reject are functions
function main(sql) {
return new Promise((resolve, reject) => {
pool.getConnection((err, connection) => {
if (err) throw err;
connection.query(sql, (errt, rows) => {
connection.release(); // return the connection to pool
if (errt) throw err;
console.log(rows);
resolve(rows);
});
});
});
}
async function queries() {
let Q = "SELECT * FROM `items`";
let result = await main(Q);
return result;
}
I believe this should work. Give it a try and good luck!

NodeJS UnhandledPromise warning when connecting to MongoDB

I am trying to connect to my MongoDB instance using nodejs. I expose the endpoint /mongo which is supposed to trigger the connection and creation of a document in the mongo db, as follows:
app.get('/mongo', (req, res) => {
try{
invoke();
} catch(err){
console.log(err);
}
res.send('all good.');
});
async function invoke() {
client.connect(err => {
const collection = client.db("CodigoInitiative").collection("Registered");
//create document to be inserted
const pizzaDocument = {
name: "Pizza",
shape: "round",
toppings: [ "Pepperoni", "mozzarella" ],
};
// perform actions on the collection object
const result = collection.insertOne(pizzaDocument);
console.log(result.insertedCount);
//close the database connection
client.close();
});
}
When I hit the endpoint though, it returns with the following error:
(node:15052) UnhandledPromiseRejectionWarning: MongoError: topology was destroyed. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
I'm confused because the method invocation was wrapped around a try/catch block, even though the error log claims it wasn't. Where did I go wrong here?
There could be connection error in your environment. And the error was a rejected promise, you cannot catch it via the try / catch block because the error was generated on asynchronous call stack.
an async function should alway return a promise:
async function invoke () {
return new Promise((resolve, reject) => {
client.connect(err => {
if (err) return reject(err)
...
})
})
}
the returned promise should be handled with .catch:
app.get('/mongo', (req, res) => {
invoke().then(() => res.send('all good'))
.catch(err => console.log('invoke error:', err))
})

Async function does not wait for await function to end

i have an async function that do not work as expected, here is the code :
const onCreateCoachSession = async (event, context) => {
const { coachSessionID } = context.params;
let coachSession = event.val();
let opentokSessionId = 'prout';
await opentok.createSession({ mediaMode: 'relayed' }, function(
error,
session
) {
if (error) {
console.log('Error creating session:', error);
} else {
opentokSessionId = session.sessionId;
console.log('opentokSessionIdBefore: ', opentokSessionId);
const sessionId = session.sessionId;
console.log('Session ID: ' + sessionId);
coachSession.tokbox = {
archiving: true,
sessionID: sessionId,
sessionIsCreated: true,
};
db.ref(`coachSessions/${coachSessionID}`).update(coachSession);
}
});
console.log('opentokSessionIdEnd: ', opentokSessionId);
};
My function onCreateCoachSession trigger on a firebase event (it's a cloud function), but it does not end for opentok.createSession to end, i don't understand why as i put an await before.
Can anyone have an idea why my code trigger directly the last console log (opentokSessionIdEnd)
Here is a screenshot on order of console.log :
It's probably a simple problem of async/await that i missed but i cannot see what.
I thanks in advance the community for the help.
You're using createSession in callback mode (you're giving it a callback function), so it doesn't return a Promise, so it can't be awaited.
Two solutions :
1/ Use createSession in Promise mode (if it allows this, see the doc)
let session = null;
try{
session = await opentok.createSession({ mediaMode: 'relayed' })
} catch(err) {
console.log('Error creating session:', error);
}
or 2/ await a Promise
let session;
try {
session = await new Promise((resolve, reject) => {
opentok.createSession({ mediaMode: 'relayed' }, (error, session) => {
if (error) {
return reject(error)
}
resolve(session);
})
})
} catch (err) {
console.log('Error creating session:', err);
throw new Error(err);
}
opentokSessionId = session.sessionId;
console.log('opentokSessionIdBefore: ', opentokSessionId);
// ...
await means it will wait till the promise is resolved. I guess there is no promise returned in this case. you can create your own promise and handle the case

Global connection already exists despite sql.close()

I created query function to connect to mssql database like below:
query(query) {
return sql.connect(config.connection.sqlserver).then(() => sql.query(query)).then((result) => {
sql.close();
return result.recordset;
}).catch((err) => console.log(err));
},
When I'm starting nodejs server everything works fine. When I'm doing refresh many times I'm getting result from database e.g.
But when I'm sending request from client side to server side I'm getting an error like below:
(node:19724) UnhandledPromiseRejectionWarning: Error: Global connection already
exists. Call sql.close() first.
at Object.connect (C:\repos\mtg-app\node_modules\mssql\lib\base.js:1723:31)
at query (C:\repos\mtg-app\server\query.js:6:16)
at asyncCall (C:\repos\mtg-app\server\routes\index.js:19:11)
at router.get (C:\repos\mtg-app\server\routes\index.js:29:3)
at Layer.handle [as handle_request] (C:\repos\mtg-app\node_modules\express\l
ib\router\layer.js:95:5)
at next (C:\repos\mtg-app\node_modules\express\lib\router\route.js:137:13)
at Route.dispatch (C:\repos\mtg-app\node_modules\express\lib\router\route.js:112:3)
at Layer.handle [as handle_request] (C:\repos\mtg-app\node_modules\express\lib\router\layer.js:95:5)
at C:\repos\mtg-app\node_modules\express\lib\router\index.js:281:22
at Function.process_params (C:\repos\mtg-app\node_modules\express\lib\router\index.js:335:12)
(node:19724) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
(node:19724) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
I don't understand first of all why it's working only on server and secondly why it's not working despite sql.close() method?
Please explain me this issue very well.
In regards to my comment, something along these lines.
async function query(q) {
return new Promise((resolve, reject) => {
new sql.ConnectionPool({/* Config */}).connect().then((pool) => {
return pool.request().query(q)
}).then((result) => {
// Resolve result
resolve(result);
// Close sql.close();
sql.close();
}).catch((error) => {
// Reject error
reject(error);
// Close sql.close();
sql.close();
});
});
}
query(`SELECT * FROM`)
.then((response) => {
// Success
console.log(response);
})
.catch((error) => {
// Error
console.log(error);
})
.finally(() => {
// Clean-up
});
Or another way, with mysqlijs all from the documentation here.
const mysql = require('mysql');
const pool = mysql.createPool({/* Config */});
pool.getConnection(function(error, connection) {
if (error) {
throw error;
}
// Use the connection
connection.query(`SELECT * FROM`, function (error, results, fields) {
// When done with the connection, release it.
connection.release();
// Handle error after the release.
if (error) throw error;
// Don't use the connection here, it has been returned to the pool.
});
});
pool.on('acquire', function (connection) {
console.log('Connection %d acquired', connection.threadId);
});
pool.on('enqueue', function () {
console.log('Waiting for available connection slot');
});
pool.on('release', function (connection) {
console.log('Connection %d released', connection.threadId);
});
pool.end(function (err) {
// Ending all connection the the MySQL pool.
});
I did it in the simpler way than below. But thanks Raymond because I realized that I should use ConnectionPool.
My query module:
/* eslint-disable import/no-unresolved */
const mssql = require('mssql');
const database = require('./config/connection');
const pool = new mssql.ConnectionPool(database.config).connect();
async function query(sql) {
try {
const connection = await pool;
const result = await connection.query(sql);
return result.recordset;
} catch (err) {
console.log('SQL error', err);
}
return [];
}
module.exports.query = query;
Now when I want to use it e.g. in router module:
router.get('/get-users', (req, res, next) => {
const usersStandings = [];
sql.query('select u.name, s.points from dbo.[user] u join dbo.standings s on s.id = u.standings_id join dbo.category c on c.id = s.category_id where c.type = \'Tournament\' order by points desc').then((rows) => {
rows.forEach((element) => {
usersStandings.push(element);
});
res.send(usersStandings);
});
});
Now I don't have problems with Global connection etc.

UnhandledPromiseRejectionWarning in Express App

I'm currently learning Javascript/Node.js/MEAN stack and I'm following an Express tutorial.
I get the following error:
(node:11524) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): TypeError: Cannot read property 'close' of undefined
(node:11524) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
When I hit this route in my browser.
function router(nav) {
adminRouter.route('/')
.get((req, res) => {
const url = 'mongodb://localhost:27017';
const dbName = 'libraryApp';
(async function mongo() {
let client;
try {
client = await mongoClient.connect(url);
debug('Connected correctly to server');
const db = client.db(dbName);
const response = await db.collection('books').insertMany(books);
res.json(response);
} catch (err) {
debug(err.stack);
}
client.close();
}());
});
return adminRouter;
}
Could someone point out the issue and explain what the issue is please.
If this line rejects:
client = await mongoClient.connect(url);
Then, you go to your catch block and after that catch block, you call client.close(). But, client is undefined so client.close() will throw and you are not inside any sort of try/catch at that point. Since you're inside an async function, that throw will turn into a rejected promise which you have no .catch() to handle. Thus, you end up with an unhandled promise rejection.
You should be able to fix it like this:
function router(nav) {
adminRouter.route('/').get(async (req, res) => {
const url = 'mongodb://localhost:27017';
const dbName = 'libraryApp';
let client;
try {
client = await mongoClient.connect(url);
debug('Connected correctly to server');
const db = client.db(dbName);
const response = await db.collection('books').insertMany(books);
res.json(response);
} catch (err) {
debug(err.stack);
// make sure and send some response on errors
res.sendStatus(500);
}
if (client) {
client.close();
}
});
return adminRouter;
}
This makes several changes:
Add if (client) before calling client.close() to protect it from the case where `client never got set.
Make the whole .get() callback be async rather then using an IIFE (not required, just seems cleaner to me)
Send an error status and response in your catch statement so you are always sending an http response of some kind, even in error situations.
If you really wanted to be fail-safe, you could just add another try/catch:
function router(nav) {
adminRouter.route('/').get(async (req, res) => {
const url = 'mongodb://localhost:27017';
const dbName = 'libraryApp';
let client;
try {
client = await mongoClient.connect(url);
debug('Connected correctly to server');
const db = client.db(dbName);
const response = await db.collection('books').insertMany(books);
res.json(response);
} catch (err) {
debug(err.stack);
// make sure and send some response on errors
res.sendStatus(500);
}
try {
if (client) {
client.close();
}
} catch(e) {
console.log("unable to close database connection");
}
});
return adminRouter;
}

Categories

Resources