I'm trying to rewrite my code to incorporate promises. I know that mongo already incorporates promises but i'd like to understand promises a bit more first. I don't understand the error message because I have used await in the async function. I found this articles that seems to do it similarly, but I still wasn't able to get it working.
What am i doing incorrectly here?
error message
SyntaxError: await is only valid in async function
code
app.post('/search/word',urlencodedParser, async function(req, res){
try{
MongoClient.connect(url, { useNewUrlParser: true }, function(err, db) {
if (err) throw err;
let dbo = db.db("words");
//Declare promise
let searchWord = function(){
return new Promise(function(resolve, reject){
dbo.collection("word").find({"$text": {"$search": req.body.word}})
.toArray(function(err, result) {
err ? reject(err) : resolve(result);
});
});
};
result = await searchWord();
db.close();
res.setHeader('Content-Type', 'application/json');
res.send(JSON.stringify(result));
});
} catch(e) {
console.log(e);
}
});
The callback functions needs to be async
app.post('/search/word',urlencodedParser, async function(req, res){
try{
MongoClient.connect(url, { useNewUrlParser: true }, async function(err, db) {
if (err) throw err;
let dbo = db.db("words");
//Declare promise
let searchWord = function(){
return new Promise(function(resolve, reject){
dbo.collection("word").find({"$text": {"$search": req.body.word}})
.toArray(function(err, result) {
err ? reject(err) : resolve(result);
});
});
};
result = await searchWord();
db.close();
res.setHeader('Content-Type', 'application/json');
res.send(JSON.stringify(result));
});
} catch(e) {
console.log(e);
}
});
Related
everyone! I have problem with async/await functions.
This is code from my backend. I try to save information of the person who just registered.
I really don't understand why Redirect() function executes before functions with await. All executes in async function. Main problem of this is empty "login","email" and user's profile picture sections on the profile page. Profile page gets login, email and profile from server with AJAX and when server redirects to this page nothing is loaded. But if I refresh profilePage, login, email and profile picture show up, what tells me that it is just haven't downloaded yet when redirection starts. It would be great if someone, who understand what is going on, will explain situation
app.post('/regPage.html', (req, res) => {
var form = new formidable.IncomingForm({
uploadDir: __dirname + '\\uploads',
keepExtensions : true,
maxFileSize : 10*1024*1024,
multiples : true,
});
console.log(form);
form.parse(req, async function (err, fields, files) {
if(err) console.error(err);
let userData = fields;
console.log(fields);
console.log(files);
async function createUserFolder() {
fs.mkdir(path.join(__dirname, `/users/${fields.login}`),{ recursive: true }, err => {
if (err) throw err;
console.log('User folder created...');
return;
});
}
await createUserFolder();
console.log(typeof fields);
console.log('Right after consolelog files');
async function writingDataToFolders() {
fs.writeFile(path.join(__dirname, `/users/${fields.login}`, `${fields.login}.json`), JSON.stringify(userData),
err => {
if (err) throw err;
console.log("User file created...")
}
);
//if(files.profilePic.originalFilename.split('.').pop() == undefined) {
fs.rename(files.profilePic.filepath, path.join(__dirname, `/users/${fields.login}`, `${fields.login}_profilePic.${files.profilePic.originalFilename.split('.').pop()}`), function(err){
if(err) {
console.log(err);
fs.unlink(files.profilePic.filepath, err => {
if(err) throw err;
console.log('Rename error on no profile pic');
});
}
console.log("Successfully uploaded");
return;
});
//}
}
// Data entry in MySQL, cookie generating
let cookieId = uuid.v4();
console.log(cookieId);
async function writingDataToDatabase() {
let sql = 'SELECT cookieId FROM profiles WHERE cookieId= ?;';
let query = db.query(sql, cookieId, (err, results) => {
if(err) throw err;
console.log(results);
if(results == "") {
console.log('No such CookieId in database!...');
const profileObjForMysql = {
login : fields.login,
email : fields.email,
password : fields.password,
cookieId : cookieId
}
//setCookie('ID', cookieId, 365);
sql = `INSERT INTO profiles SET ?;`;
query = db.query(sql, profileObjForMysql, (err, result) => {
if(err) throw err;
console.log(result);
console.log('Profile added to MySQL...');
return;
});
}
});
}
async function createNewUserEndpoints() {
let newUserEndpont =`\n \n router.get(\'/${fields.login}/${fields.login}_profilePic.${files.profilePic.originalFilename.split('.').pop()}\', (req, res) => {
res.sendFile(path.join(__dirname, \"/${fields.login}/${fields.login}_profilePic.${files.profilePic.originalFilename.split('.').pop()}\"));
console.log(\'Hello\');
});
router.get(\'/${fields.login}/${fields.login}.json\', (req, res) => {
res.sendFile(path.join(__dirname, \'/${fields.login}/${fields.login}.json\'));
}); \n`;
fs.appendFile(path.join(__dirname, `/users/users_router.js`,), newUserEndpont ,(err) => {
if(err) throw err;
return;
});
console.log('New endpoint for user added...');
}
res.cookie('ID', cookieId, {httpOnly: false, maxAge : 999999999});
await writingDataToFolders();
console.log("Data was written to foldres...");
await writingDataToDatabase();
console.log("Data was written to Database...");
await createNewUserEndpoints();
console.log("New endpoint for user was created...");
res.redirect(301, '/profilePage.html');
});
});
I also tried contain all "await function_name" into function like this:
async function redirect() {
await writingDataToFolders();
console.log("Data was written to foldres...");
await writingDataToDatabase();
console.log("Data was written to Database...");
await createNewUserEndpoints();
console.log("New endpoint for user was created...");
res.redirect(301, '/profilePage.html');
}
redirect();
but it didn't work too
Your async functions are not really asynchronous, because you don't await the callback. One way to achieve this is to use util.promisify, which converts the callback-flavored function into a function returning a promise, which you can await:
async function createUserFolder() {
await util.promisify(fs.mkdir)(
path.join(__dirname, `/users/${fields.login}`),
{ recursive: true }
).then(() => {
console.log('User folder created...');
});
}
In other words: fs.mkdir is a function that executes asynchronously and invokes a callback when done. util.promisify(fs.mkdir) is a function that returns a promise, which resolves when the asynchronous execution is done.
By contrast, fs.mkdirSync is a synchronous function which blocks the Node.js event loop. This should be avoided if you expect many parallel requests to your server.
My backend is consist of Api and DB. When I want to get response from DB I have had delayed output by 1 query.
API (I think api is ok. Start read DB first)
app.post('/api/query', (req, res) => {
console.log(`\n Query input : ${JSON.stringify(req.body)}`);
let queryInput = (Object.values(req.body).join(' '));
if(!dbApi.checkArray(queryInput)){ //If array is not made from clear strings
res.json(dbApi.queryFromUser(queryInput));
}
else{
res.json(dbApi.queryOutput);
}
});
app.listen(dbConfig.server.port, () =>
console.log(`Server running on port ${dbConfig.server.port}`));
DB
queryOutput = [];
const receivingQuery =(queryInput) => {
db.query(queryInput, (err, result) =>{
if(err) throw err+' : '+queryInput;
queryOutput = result;
console.log("\nQuery output "+ JSON.stringify(queryOutput)); //Output (result) is ok
});
return queryOutput //Here is Output from previous query (sends to API)
}
module.exports = {
queryOutput: queryOutput,
queryFromUser: receivingQuery,
}
I tryied callback method and I rewrite it couple of times. But I dont have enough skill to solve it.
If You want to return result of query so simply do following things:
add query method to db module:
function query(sql, args = []) {
return new Promise(function(resolve, reject) {
db.query(sql, args, (err, result) => {
if (err) return reject(err);
resolve(result);
});
});
}
// extra feature, getting user by id
async function getUserById(id) {
const result = await query('SELECT * FROM users WHER id = ? LIMIT 1', [id]);
if (Array.isArray(result) && result[0]) return result[0];
return null;
}
module.exports = {
query,
getUserById, // export user by id
queryOutput,
queryFromUser: receivingQuery,
}
use it (with async and await):
app.post('/api/query', async (req, res) => {
try {
console.log('Query input:', req.body);
const queryInput = Object.values(req.body).join(' ');
const result = await dbApi.query(queryInput);
res.json(result);
}
catch (error) {
console.error(error);
res.status(500).json({message: 'Please try again soon'});
}
});
app.get('/api/users/:id', async (req, res) => {
try {
const user = await dbApi.getUserById(req.params.id);
if (!user) return res.status(404).json({message: 'User not found'});
res.status(200).json(user);
}
catch (error) {
console.error(error);
res.status(500).json({message: 'Please try again soon'});
}
});
app.listen(dbConfig.server.port, () =>
console.log('Server running on port', dbConfig.server.port));
I recently have been developing a web app with NODEJS. I have a db model that takes care of all crud operations. Sample DB model
getDeviceList: function () {
return new Promise(function (resolve, reject) {
var sql = "SELECT * FROM `devices`";
db.query(sql, function (err, result) {
if (err) {
console.log(`FAILED: ${err}`);
reject(err);
} else resolve(result);
});
});
}
And I access this function in DB module like
router.get("/inventory/devices", checkAuth, function (req, res, next) {
return new Promise(function (resolve, reject) {
db.getDeviceList().then(function (result) {
resolve(res.status(200).json(result));
}).catch(function (err) {
console.log(`FAILED: ${err}`);
reject(res.status(200).json(err));
});
});
});
Sometimes when I close the DataBase; I get UnhandledPromiseRejectionWarning but In my eyes I am handling all the promises. What is the best way of handling promises and is my method of retrun promise correct?
My DB connection block is in app.js
var connection = db.connectDB().then(function (connect) {}).catch(function (err) {
console.log(`FAILED: ${err}`);
});
and my connectDB() is
function () {
return new Promise(function (resolve, reject) {
db = mysql.createPool({
host: "localhost",
port: 3306,
user: "migration",
password: "iub5015",
database: "migration"
});
db.getConnection(function (err) {
if (err) {
console.log(`FAILED: ${err}`);
reject(err);
} else {
console.log("Connected");
resolve(db);
}
});
});
}
The last statement in your catch block,
reject(res.status(200).json(err));
means that the returned promise is in rejected state. ie the Promise returned by router.get("/inventory/devices", checkAuth, function (req, res, next) is in rejected state and you have missed to catch that. (or express does not catch that as it seems in your case)
This is why you are getting this UnhandledPromiseRejectionWarning.
What is the best way of handling promises and is my method of retrun
promise correct?
Well, in my experience it depends. In some scenarios you may want to propagate error by rejecting Promise, or in some case you may just want to handle it some way and return something as resolved value. In your case you may have to look into express docs to figure out how you need to implement promises within your middleware.
router.get("/inventory/devices", checkAuth, function (req, res, next) {
return new Promise(function (resolve, reject) {
db.getDeviceList().then(function (result) {
resolve(res.status(200).json(result));
}).catch(function (err) {
console.log(`FAILED: ${err}`);
// This will not give UnhandledPromiseRejectionWarning
resolve(res.status(500).json(err));
});
});
});
router.get("/inventory/devices", checkAuth, function (req, res, next) {
new Promise(function (resolve, reject) {
db.getDeviceList().then(function (result) {
resolve(result);
}).catch(function (err) {
console.log(`FAILED: ${err}`);
reject(err);
});
}).then(r => {
res.status(200).json(r)
}).catch(er => {
res.status(400).json(er)
})
});
I'm calling a function to see if an email adress is in the db. It's either an empty or filled array. Whenever i return this value it is undefined afterwards. How can I solve this? Thanks in advance!
I have tried using next() and promises.
The function that calls the db query function.
const emailExists = async function(req,res,next){
let emailInDb = await usermodel.getOneByEmail(req, next);
console.log(emailInDb);
if(emailInDb.length !==0){
res.send('email already in use!');
}
else{
return next();
}
};
The db query.
const getOneByEmail = function (req, next){
let db = new sqlite3.Database(dbPath, (err) => {
if (err) {
console.error(err.message);
}
console.log('Connected to books db.');
});
db.all(`SELECT * FROM users WHERE email = ?`, [req.body.email],
(err, rows) => {
console.log(rows);
return rows;
});
};
console.log(emailInDb) gives undefined.
While console.log(rows) gives an array with results.
Putting await in front of a non async function does not magically make the function wait for the db-calls to resolve. You need to wrap the db-calls in a promise, e.g:
const getOneByEmail = (req, next) => {
return new Promise((resolve, reject) => {
let db = new sqlite3.Database(dbPath, (err) => {
if (err) {
console.error(err.message);
reject(err);
}
console.log('Connected to books db.');
});
db.all(`SELECT * FROM users WHERE email = ?`, [req.body.email],
(err, rows) => {
// TODO: check for error here and reject
console.log(rows);
resolve(rows);
});
});
};
Note that you shouldn't connect to the database each time you call getOneByEmail, instead reuse the connection...
I am trying to do a nested query with MySql, put the result inside a variable and send over http, but the program always run console.log("test 2:"+rpsData); before the query finish. I already tried this, but still getting the same problem.
const express = require('express')
const app = express()
const mysql = require('mysql');
const Connection = require('mysql/lib/Connection');
const Promise = require('bluebird');
Promise.promisifyAll([
Connection
]);
const connection = mysql.createConnection({
host : 'localhost',
user : 'root',
password : 'root123',
database : 'mygallery'
});
app.get('/posts', function(request, response) {
var rpsData;
connection.connectAsync()
.then(function() {
connection.query('SELECT * FROM post WHERE approved = 1', function(err, rows, fields) {
if (err) throw err;
rpsData = rows;
for (var i in rows) {
connection.query('SELECT * FROM image WHERE postCode = ?', [rpsData[i].postCode], function(err, rows, fields) {
if (err) throw err;
rpsData[i].image = rows;
console.log("test 1:"+rpsData);
});
}
});
})
.then(function() {
response.send(rpsData);
console.log("test 2:"+rpsData);
})
.catch(function(error) {
console.error('Connection error.', error);
});
});
What's happening here is you're not tying all of the pieces of async code to the promise chain. Once we convert it to do so this should work.
First lets wrap calls to connection.query to return a promise. We then have to return that generated promise to attach it to the outer promises chain.
If you don't return a promise, it won't know that it has to wait for your code to finish executing and will move forward with the next .then() statement on the outside promise (from connection.connectAsync);
You need to apply the same treatment to the inner query.
Sample code:
app.get('/posts', function(request, response) {
connection.connectAsync()
.then(function() {
return new Promise(function(resolve, reject) {
connection.query('SELECT * FROM post WHERE approved = 1', function(err, rows, fields) {
if (err) reject(err);
resolve(rows.reduce(function(accumulator, current) {
return accumulator.then(function(rpsData){
return new Promise(function(resolve, reject){
connection.query('SELECT * FROM image WHERE postCode = ?', [current.postCode], function(err, rows, fields) {
if (err) reject(err);
current.image = rows;
console.log("test 1:"+rpsData);
resolve(rpsData);
});
});
});
}, Promise.resolve(rows)));
});
});
})
.then(function(rpsData) {
response.send(rpsData);
console.log("test 2:"+rpsData);
})
.catch(function(error) {
console.error('Connection error.', error);
});
});
I'm queueing the internal promises using the technique I describe here