I have a method that selects distinct values from a database as shown below:
function displayCategories(res, req) { query = `SELECT DISTINCT name FROM product_category;`; connection.query(query, function (err, rows) { if (err) { console.log(err); res.render("home"); throw err; } else { session = req.session; session.categories = rows[0]; } }); }
I then have a button with the method POST
and action /categories
The displayCategories
is called when the button is clicked as follows:
router.post('/categories', function (req, res) { displayCategories(res, req); if (session.categories === undefined) { console.log("categories is undefined"); } else { console.log("categories is defined"); console.log(session.categories); } })
I added some console logs for test purposes. The issue I am having is that the first time I click the button, it returns undefined. Each time I click it again, it prints the correct data for session.categories
as shown below:
Is there a simple fix for this issue?
Advertisement
Answer
The code is calling a displayCategories
as if it were synchronous, but it is running asynchronous code with the callback.
There are multiple possible solutions for that but one of them would be to use Promises, like the following:
const displayCategories = (res, req) => new Promise((resolve, reject) => { // you are not declaring query in this scope, that makes it global query = `SELECT DISTINCT name FROM product_category;` connection.query(query, function (err, rows) { if (err) { console.error(err) res.render("home") reject(err) } else { session = req.session session.categories = rows[0] resolve() } }) })
And the other part with an async function
router.post('/categories', async function (req, res) { await displayCategories(res, req); if (session.categories === undefined) { // session is not declared console.log("categories is undefined"); } else { console.log("categories is defined"); console.log(session.categories); // session is not declared } })
But that’s just to make your issue go away, if you want to improve the code even further you can just keep the responsibility of dealing with request and response with the controller action and just use the other function to get the data you want, isolation its responsibility:
const getCategories = () => new Promise((resolve, reject) => { const query = `SELECT DISTINCT name FROM product_category;` connection.query(query, (err, rows) => { if (err) return reject(err) resolve(rows) }) }) router.post('/categories', async function (req, res) { try { req.session.categories = await getCategories(); if (req.session.categories === undefined) { console.log("categories is undefined"); } else { console.log("categories is defined", req.session.categories); console.log(); } } catch(e) { console.error(e) res.render("home") } })