Issue
I’m trying to write a function that returns a query from a sqlite3 database (using Node and Express)
This is how (likely) the function is called
app.get('/example',(req,res)=>{
console.log(getThings(db_connection))
}
And this is the function per se
getThings(db){
let sql = 'SELECT * FROM Table'
let results[]
db.all(sql, (err, rows) => {
if(err){throw err}
let i
for(i=0;i<rows.length;i++){
res.push(rows[i])
}
console.log(res)
})
return res
}
I expected the rows being returned at the end, but it always returns res before populating it first, and just then it prints res with the correctly
I might have understood why it does so, but I have no idea how to fix it properly (I’m still new at JS)
Solution
Callbacks are asynchronous, so res
will not be populated before the return.
You need to make your callback into a Promise or use async/await.
Promisify the callback:
getThings(db){
return new Promise((resolve, reject) => {
db.all('SELECT * FROM Table', (err, rows) => {
if (err) reject(err)
resolve(rows)
})
})
}
app.get('/example', (req, res) => {
getThings(db_connection)
.then(rows => res.send(result))
.catch(err => res.send({
error: err.message
}))
}
or
Use async/await:
Wrapped in try/catch to catch err
, then because you’re simply looping over the rows you don’t need the for
loop.
When you see the following structure object.method('param', (err, rows) => {
you can almost guarantee its Promise compatible, else if not you can use util.promisify(original) to make it so.
Cleaner and more verbose then then().catch() chaining:
app.get('/example', async (req, res) => {
let result = {}
try {
result = await getThings(db_connection)
} catch (err) {
result = {
error: err.message
}
}
res.send(result)
}
async getThings(db) {
return db.all('SELECT * FROM Table')
}