I'm having issues with getting the result of a callback function. Below is the async function that I'm calling
const utils = {
sendQuery: async function(query){
// Receives a query and returns raw results
// Query is using default database specified by pool
// Returns a Promise
let conn;
try {
conn = await pool.getConnection();
let queryString = query;
let rows = await conn.query(queryString);
let results = (this.formatResults(rows));
console.log(results);
return results;
} catch(err) {
throw new Error(err);
} finally {
if (conn) return conn.end();
}
}
module.exports = {
'utils': utils
}
the console log above returns the expected result.
and below is the function that calls the above
const db = require('../private/db');
db.utils.sendQuery(queryString).then(function(result){
console.log(result);
}).catch(err=>{
throw res.render('error', {'error': err.stack});
})
the console log above returns undefined and I have no idea why.
The real problem here is this part if (conn) return conn.end();.
Whenever you are using finally, it will override any previous return, break, continue or throw that happens either in the stated try or catch blocks.
To fix your issue you should do like so:
const utils = {
sendQuery: async function(query){
// Receives a query and returns raw results
// Query is using default database specified by pool
// Returns a Promise
let conn;
try {
conn = await pool.getConnection();
let queryString = query;
let rows = await conn.query(queryString);
let results = (this.formatResults(rows));
console.log(results);
return results;
} catch(err) {
throw new Error(err);
} finally {
if (conn) conn.end();
}
}
module.exports = {
'utils': utils
}
Hope it works
In my opinion, just return results instead of resolve(results). Your function is already async and no promise object is created here.
And just throw err instead of reject(err);
And since you return in your try, you don't need your finally statement.
You need to simply return the result instead of calling resolve
const utils = {
sendQuery: async function(query){
// Receives a query and returns raw results
// Query is using default database specified by pool
// Returns a Promise
let conn;
try {
conn = await pool.getConnection();
let queryString = query;
let rows = await conn.query(queryString);
let results = (this.formatResults(rows));
console.log(results);
return results;
} catch(err) {
throw new Error(err)
} finally {
if (conn) return conn.end();
}
}
module.exports = {
'utils': utils
}
you could simply return or i suppose this is what you were trying to do
sendQuery: (query) => {
let promise = new Promise(async (resolve, reject) => {
let conn;
try {
conn = await pool.getConnection();
let queryString = query;
let rows = await conn.query(queryString);
let results = (this.formatResults(rows));
console.log(results);
resolve(results);
} catch (err) {
reject(err);
} finally {
if (conn) {
conn.end();
}
}
})
return promise;
}
Related
So, im my nodeJS project I have this part of my code, that is not waiting for the await call
Controller.js
exports.getList = async function(request, response) {
try {
const result = await useCase.getList()
if (result == undefined) {
utils.unavailable(response)
}
else if (result == null || result.length == 0) {
utils.respond(response, 404, errCodes.NOT_FOUND)
}
else utils.respond(response, 200, result)
}
catch(e) {
utils.unavailable(response)
}}
This is the function on the controller, it calls the response from the useCase
UseCase.js
exports.getList = async function() {
return await new Promise(function(resolve) {
resolve(repository.getList())
})
Which also calls the repository
Repository.js
exports.getList = async function() {
MongoClient.connect(Constants.DB_URL, function(err, db) {
if (err) {
console.log(err)
return undefined
}
const dbo = db.db(Constants.DB_NAME)
dbo.collection(Constants.DB_FEATURE1_COLLECTION).find({}).toArray(function(err, result) {
if (err) {
console.log(err)
return undefined
}
db.close()
return result
})
});
So, what happens is that on the controller, the result is always undefined, since result hasn't been initialized, when it should have actually waited for the response of the Promise on the UseCase. So what am I doing wrong?
getList function does not have return statement, and since it is async it returns a Promise that always resolves with undefined. I am not very familiar with MongoClient, but documentation says it returns a promise if no callback is specified. So you can change your code:
exports.getList = async function () {
try {
const db = await MongoClient.connect(Constants.DB_URL);
const dbo = db.db(Constants.DB_NAME);
const result = await dbo.collection(Constants.DB_FEATURE1_COLLECTION).find({}).toArray();
db.close();
return result
} catch (err) {
console.log(err);
return undefined
}
};
and UseCase.js:
exports.getList = function() {
return repository.getList(); // it returns a Promise there is no need to wrap it in async
};
Here is the index.ts script I am running (based on something I found on reddit):
const path = require("path");
const sql = require("mssql");
const config = require(path.resolve("./config.json"));
let db1;
const connect = () => {
return new Promise((resolve, reject) => {
db1 = new sql.ConnectionPool(config.db, err => {
if (err) {
console.error("Connection failed.", err);
reject(err);
} else {
console.log("Database pool #1 connected.");
resolve();
}
});
});
};
const selectProjects = async (name) => {
const query = `
select * from [Time].ProjectData where [Name] like concat('%', concat(#name, '%'))`;
const request = new sql.Request(db1);
const result = await request
.input("name", name)
.query(query);
return result.recordset;
};
module.exports = {
connect,
selectProjects
};
connect().then(function() {
console.log(selectProjects('General'));
}).catch(function(err) {
console.log(err);
});
When I run the script using node index (after compiling it of course), I get this in the console:
Database pool #1 connected.
Promise { <pending> }
And then the script hangs.
Apparently the await keyword creates an implicit promise; I had to change the last function call to:
connect().then(function() {
selectProjects('General').then(function(data) {
console.log(data);
});
}).catch(function(err) {
console.log(err);
});
I am getting error of Error: Timeout of 2000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves.
This makes sense, if returning a promise to ensure it resolves, but I really thought that I do that already (by using the await keyword). There seems to be a race condition here that I don't understand
I am writing unit test for a database connection module:
states.js
const sqlite3 = require('sqlite3').verbose();
const util = require('util');
async function getDB() {
return new Promise(function(resolve, reject) {
let db = new sqlite3.Database('./project.db', (err) => {
if (err) {
console.error(err.message);
reject(err)
} else {
console.log('Connected to the project database.');
resolve(db)
}
});
return db
});
}
exports.getDB = getDB
try {
// run these statements once to set up the db
// let db = getDB();
// db.run(`CREATE TABLE services(id INTEGER PRIMARY KEY, service text, date text)`);
// db.run(`INSERT INTO services(id, service, date) VALUES (1, 'blah', '01-23-1987')`)
} catch(err) {
console.log(err)
}
exports.get = async function(service) {
function getResults(service) {
return new Promise(async function (resolve, reject) {
const db = await getDB();
let sql = `SELECT Id id,
Service service,
Date date
FROM services
WHERE service = ?`;
db.get(sql, [service], (err, row) => {
if (err) {
console.error(err.message);
reject(err)
} else {
if (row) {
let this_row = {'id': row.id, 'service': row.service, 'date': row.date};
this_row ? console.log(row.id, row.service, row.date) : console.log(`No service found with the name ${service}`);
resolve(this_row)
} else {
resolve(null)
}
}
})
});
}
let row = await getResults(service)
return row
}
exports.clear = async function(service) {
function deleteResults(service) {
return new Promise(async function (resolve, reject) {
const db = await getDB();
let sql = `DELETE from services
WHERE service = ?`;
db.run(sql, [service]);
});
}
await deleteResults(service)
}
my testStates.js:
const mocha = require('mocha');
const assert = require('assert');
const expect = require('chai').expect;
const should = require('chai').should();
const state = require('../state');
let deletion_sql = `DELETE from services WHERE service = ?`;
it("get() should return the expected row", async function() {
let db = await state.getDB()
await db.run(deletion_sql, 'blah')
await db.run(`INSERT INTO services(id, service, date) VALUES (1, 'blah', '01-23-1987')`)
let result = await state.get('blah')
console.log("get test result is")
console.log(result)
assert.deepEqual(result, { 'id': 1, 'service': 'blah', 'date': '01-23-1987' })
});
it("clear() should delete row from db", async function() {
await state.clear('blah')
let result = await state.get('blah')
assert.equal(result, null)
})
The test clear() should delete row fromdb` fails every time with the same error, it has not passed once. There is something fundamentally wrong with how I use these promises since I'm new to learning about promise in JavaScript.
I was able to get test running by using .then() instead of the await keyword:
it("clear() should delete row from db", async function() {
state.clear('blah').then(async function() {
let result = await state.get('blah')
assert.equal(result, null)
})
})
I am interested to find out why the await keyword didn't work here, since it worked in the other test; I would gladly accept that as answer.
I have this request handler on my node server. It has three MongoDB queries, and I want all the results to be returned, before the response is sent.
api.get('/getStats/:productID', (req,res)=>{
let data = {};
let dailySales = [];
let avgProduct = "";
let customers = [];
Sales.find({productID: productID}).then(
sales => {
dailySales = sales;
}
);
Products.find({}).then(
products => {
// Calculate Avg product here
avgProduct = result;
}
);
Customers.find({}).then(
customers => {
customers = customers;
}
);
data = {
dailySales,
avgProduct,
customers
};
res.json(data);
});
But running this returns
data: {
dailySales: [],
avgProduct: "",
customers: []
}
i.e. The Mongo response is returning before the data is run. Please how to I fix. Thank You
wait for all the promises to resolve before sending the actual response
const sales = Sales.find({productID: productID});
const allProducts = Products.find({});
const allCustomers = Customers.find({});
Promise.all([sales, allProducts, allCustomers])
.then(data => res.json(data));
you can try using the Promise.all where you can pass the MongoDB queries as parameter to it ,the promise will be resolved when all the queries return the result in the array
Try using the in-built util.promisify function along with
async-await to get data correctly!
const promisify = require('utils').promisify;
const salesFindOnePromise = promisify(Sales.find);
const productsFindAllPromise = promisify(Products.find);
const customersFindAllPromise = promisify(Customers.find);
findDailySalesByIdAsync = async (productID) => {
try {
return await salesFindOnePromise({ productID: productID });
} catch(err) {
throw new Error('Could not fetch the appropriate sales with productID');
}
}
findProductsAsync = async () => {
try {
return await productsFindAllPromise({});
} catch (err) {
throw new Error('Could not fetch sales!');
}
}
findCustomersAsync = async () => {
try {
return await customersFindAllPromise({});
} catch (err) {
throw new Error('Could not fetch customers!');
}
}
api.get('/getStats/:productID', async (req,res)=>{
try {
const dailySales = await findDailySalesByIdAsync(productID);
const avgProduct = await findProductsAsync();
const customers = await findCustomersAsync();
const data = {
dailySales,
avgProduct,
customers
};
return res.status(200).send(data);
} catch(err) {
console.err(`Failed because: {err}`);
throw new Error('Could not fetch data because of some error!');
}
});
I'm trying to return a custom object from a async function that works as wrapper for a put using indexdb.
Using Promises this is easy.
However, using async/await became more challenging...
const set = async (storeName, key, value) => {
if (!db)
throw new Error("no db!");
try {
const result = {};
let tx = db.transaction(storeName, "readwrite");
let store = tx.objectStore(storeName);
let r = store.put({ data: key, value: value });
console.log(r);
r.onsuccess = async () => {
console.log('onsuccess');
result.something = true;
}
r.onerror = async () => {
console.log('onerror');
result.something = false;
}
await r.transaction.complete; // ok... this don't work
// how can I await until onsuccess or onerror runs?
return result;
} catch (error) {
console.log(error);
}
}
The ideia is to return a composed object... however all my attemps fails as onsuccess runs after returning the result.
I googled a lot and could't find a way to proper await for onsuccess/onerror events.
I know that returning a Promise is more easy as resolve(result) would end returning what I want... but i'm trying to learn to make same code using async/await.
Thank you so much,
Try this:
function set(db, storeName, key, value) {
return new Promise((resolve, reject) => {
let result;
const tx = db.transaction(storeName, 'readwrite');
tx.oncomplete = _ => resolve(result);
tx.onerror = event => reject(event.target.error);
const store = tx.objectStore(storeName);
const request = store.put({data: key, value: value});
request.onsuccess = _ => result = request.result;
});
}
async function callIt() {
const db = ...;
const result = await set(db, storeName, key, value);
console.log(result);
}
Edit, since you insist on using the async qualifier for the set function, you can do this instead. Please note I find this pretty silly:
async function set(db, storeName, key, value) {
// Wrap the code that uses indexedDB in a promise because that is
// the only way to use indexedDB together with promises and
// async/await syntax. Note this syntax is much less preferred than
// using the promise-returning function pattern I used in the previous
// section of this answer.
const promise = new Promise((resolve, reject) => {
let result;
const tx = db.transaction(storeName, 'readwrite');
tx.oncomplete = _ => resolve(result);
tx.onerror = event => reject(event.target.error);
const store = tx.objectStore(storeName);
const request = store.put({data: key, value: value});
request.onsuccess = _ => result = request.result;
});
// We have executed the promise, but have not awaited it yet. So now we
// await it. We can use try/catch here too, if we want, because the
// await will translate the promise rejection into an exception. Of course,
// this is also rather silly because we are doing the same thing as just
// allowing an uncaught exception to exit the function early.
let result;
try {
result = await promise;
} catch(error) {
console.log(error);
return;
}
// Now do something with the result
console.debug('The result is', result);
}
Ultimately you'll end up wrapping IDB in a promise-friend library, but for your specific need, you could use something like this:
function promiseForTransaction(tx) {
return new Promise((resolve, reject) => {
tx.oncomplete = e => resolve();
tx.onabort = e => reject(tx.error);
});
}
And then in your code you can write things such as:
await promiseForTransaction(r.tx);
... which will wait until the transaction completes, and throw an exception if it aborts. (Note that this requires calling the helper
before the transaction could possibly have completed/aborted, since
it won't ever resolve if the events have already fired)
I can't confirm it right now but I think it should be await tx.complete instead of await r.transaction.complete;.
But a general solution that would work even if the API would not support Promises directly would be to wrap a new Promise around the onsuccess and onerror and use await to wait for that Promise to resolve, and in your onsuccess and onerror you then call the resolve function:
const set = async (storeName, key, value) => {
if (!db)
throw new Error("no db!");
try {
const result = {};
let tx = db.transaction(storeName, "readwrite");
let store = tx.objectStore(storeName);
let r = store.put({
data: key,
value: value
});
console.log(r);
await new Promise((resolve, reject) => {
r.onsuccess = () => {
console.log('onsuccess');
result.something = true;
resolve()
}
r.onerror = () => {
console.log('onerror');
result.something = false;
// I assume you want to resolve the promise even if you get an error
resolve()
}
})
return result;
} catch (error) {
console.log(error);
}
}
I would furhter change it to:
try {
await new Promise((resolve, reject) => {
r.onsuccess = resolve
r.onerror = reject
})
console.log('success');
result.something = true;
} catch(err) {
console.log('error');
result.something = false;
}