I have the following in Typescript:
import sql = require("mssql");
const config: sql.config = {....
}
const connect = async() => {
return new Promise((resolve, reject) => {
new sql.ConnectionPool(config).connect((err) => {
if (err) {
reject(err);
} else {
console.log("CONNECTED");
resolve();
}
});
});
};
(async() => {
await connect().then(
() => {
console.log("Connection pool created successfully.");
}).catch((err) => {
console.error(err);
});
})();
console.log("Now proceeding to load...");
I always get the console output in the following order:
Now proceeding to load...
CONNECTED
Connection pool created successfully
What have I done wrong? How do I achieve executing the last line of code only after all the activities before it have completed execution?
You're calling the (async () => {... function, which is, well, asynchronous, and then directly proceed to print the loading message.
You're also mixing and matching .then().catch() and async/await/catch error handling – I'd refactor things like this:
import sql = require("mssql");
const connect: (config: sql.config) => Promise<sql.ConnectionPool> = async config =>
new Promise((resolve, reject) => {
const pool = new sql.ConnectionPool(config);
pool.connect(err => {
if (err) return reject(err);
console.log("CONNECTED");
resolve(pool);
});
});
const config: sql.config = {
/*...*/
};
(async () => {
console.log("Now proceeding to load...");
try {
const pool = await connect(config);
console.log("Connection pool created successfully.");
} catch (e) {
console.error(e);
return;
}
})();
Try this:
(async () => {
await connect();
console.log("Connection pool created successfully.");
})();
try something like below
import sql = require("mssql");
const config: sql.config = { /*....*/ };
const connect = () => {
return new Promise((resolve, reject) => {
try {
let Connection = await sql.ConnectionPool(config).connect();
console.log("Connected to mssql");
resolve("Successfully Connected");
} catch (error) {
reject(error);
}
});
};
(async function () {
try {
let Connection = await connect();
console.log("Connection pool created successfully.");
} catch (error) {
console.error(error);
}
}());
Related
How can be resolve executed in multiple function.
As I have multiple promise function and each function contain resolve message but don't how to print this on postman
If there is single function with promise then resolve message easily get executed but what if there is function of function then how can it be possible ?
Is this possible way to return resolve or reject message from one function to another?
As I am writing to pass resolve message in postman whenever my task is completed or reject message when there is some error
But after after writing return it still not returning the resolve message or reject message inside Postman
any idea how this can be resolve?
async function readFile(filePath) {}
async function getAllFile(filePath) {
const paths = await readFile(filePath);
}
async function filterFiles(filePath) {
const paths = await getAllFile(filePath);
}
function addDocument(data){
return new Promise((resolve, reject) => {
Document.create({
name: data.name,
},
}).then(function (filePath) {
filterFiles(filePath);
let msg = "Document created Succesfully";
return resolve(msg);
})
.catch(function (err) {
return reject("Can't be updated please try again :) " + err);
});
});
}
function updateDoc(data){
return new Promise((resolve, reject) => {
Document.update({
name: data.name,
}
where: {
product_id: data,
},
})
}).then(function (filePath) {
getAllFile(filePath);
let msg = "Updated Successfully";
return resolve(msg);
})
.catch(function (err) {
return reject("Can't be updated please try again :) " + err);
});
}
function findDoc(data){
return new Promise((resolve, reject) => {
Document.findAll(
{
raw:true,
},
{
name: data.name,
}
where: {
product_id: data,
},
})
}).then(function (product) {
if(product.length===0){
addDocument(product);
let msg="task completed";
return resolve(msg,product);
else{
return resolve(updateDoc(product));
}
})
.catch(function (err) {
return reject("Can't be updated please try again :) " + err);
});
}
function findDoc(data){
return new Promise((resolve, reject) => {
Document.findAll(
where: {
product_id: data.id,
},
})
}).then(function (product) {
findDoc(product);
let msg="task completed";
return resolve(msg,product);
})
.catch(function (err) {
return reject("Can't be updated please try again :) " + err);
});
}
How can i get resolve message in postman
It's hard for me to test your codes but it's quite possible to return resolve, reject messages in the response object.
If you want to pass a reject message through 3 promise for example and return a response you need use try, catch blocks, you can check this example:
// index.js
const express = require('express')
const app = express()
const port = 3000
const responder1 = (error, message) => {
return new Promise((resolve, reject) => {
if (error) {
reject(error);
return;
}
resolve(message);
});
};
const responder2 = (error, message) => {
return new Promise((resolve, reject) => {
if (error) {
reject(error);
return;
}
resolve(message);
});
};
const responder3 = (error, message) => {
return new Promise((resolve, reject) => {
if (error) {
reject(error);
return;
}
resolve(message);
});
};
app.use('/', async (req, res) => {
let result;
try {
const error = new Error("This is a error message.");
result = await responder1(error, null);
} catch (error1) {
try {
await responder2(error1, null);
} catch (error2) {
try {
await responder3(error2, null)
} catch (error3) {
res.send({
success: 0,
message: error3.message
});
return;
}
}
}
res.send({
success: 0,
message: result
});
});
app.listen(port, () => {
console.log(`Example app listening at http://localhost:${port}`)
})
And if you want to pass resolve message:
const express = require('express')
const app = express()
const port = 3000
const responder1 = (error, message) => {
return new Promise((resolve, reject) => {
if (error) {
reject(error);
return;
}
resolve(message);
});
};
const responder2 = (error, message) => {
return new Promise((resolve, reject) => {
if (error) {
reject(error);
return;
}
resolve(message);
});
};
const responder3 = (error, message) => {
return new Promise((resolve, reject) => {
if (error) {
reject(error);
return;
}
resolve(message);
});
};
app.use('/', async (req, res) => {
let result = await responder3(null, await responder2(null, await responder1(null, "This is a test message.")));
res.send({
success: 0,
message: result
});
});
app.listen(port, () => {
console.log(`Example app listening at http://localhost:${port}`)
})
I would like to have my microservice waiting until the database becomes available.
(I have a sidecar Cloud SQL proxy that needs some time for the database connection).
So I was thinking of writing a for loop that attempts a connect and retries after a defined time.
Currently, the code looks as follows, but it doesn't seem to wait before reconnecting.
private class Database {
static async connectDatabase() {
try {
const retries = 20;
const tryTimeout = async (currentTry, retries) => {
return new Promise((resolve) =>
setTimeout(function () {
logger.info(`Try: ${currentTry}, ${retries} retries left.`);
}, 1000));
};
for (let i = 1; i <= retries; i++) {
try {
// Establish database connection
await SequelizeConnection.authenticate()
.then(() => {
logger.info(
"*** Database connection has been established successfully."
);
})
.catch(async (err) => {
logger.info("Error in connect database function: ", err);
throw err;
});
await SeqzelizeConnectionHealthcheck.authenticate()
.then(() => {
logger.info(
"*** Database connection for healthcheck has been established successfully."
);
})
.catch(async (err) => {
logger.error(
"Error in connect database function for healthcheck: ",
err
);
throw err;
});
} catch (error) {
logger.error("Error in connectDB retry function");
await tryTimeout(i, retries - i);
}
}
} catch (error) {
logger.error("Error in connect database function: ", error);
}
}
}
I was thinking of creating a retry wrapper function and tried some retry libraries but without success.
As mentioned in comments, there are a few issues in the code. Mainly that you're mixing await with .then()/.catch(), not resolving the promise from the wait and not breaking out of the for loop when the connections are successful.
To illustrate this, I've reformatted your method a bit and extracted some functions so it's hopefully clearer. In the snippet below I'm simulating the database connection succeeding after 3 tries and the healthcheck database connection succeeding after 5 tries.
I've also changed the logic a bit so that it makes the main database and healthcheck database connections concurrently and retries them independently - you don't want to be retrying the main database connection again just because the healthcheck one failed, so for this I created a retryUntilResolved function that will retry the given function until it resolves, or until the maximum number of retries has been reached.
// Mocks purely for snippet
const resolveNthTime = nth => {
let callCount = 0;
return () => ++callCount >= nth
? Promise.resolve()
: Promise.reject('failed!');
}
const SequelizeConnection = { authenticate: resolveNthTime(3) };
const SequelizeConnectionHealthcheck = { authenticate: resolveNthTime(5) };
// Example
const pause = ms =>
new Promise(resolve => setTimeout(resolve, ms));
const retryUntilResolved = (waitMs, maxRetries) => async func => {
let tries = 0;
while (true) {
try {
return await func();
} catch(err) {
if (tries++ < maxRetries) await pause(waitMs);
else return err;
}
}
};
const authenticateDatabase = async () => {
try {
await SequelizeConnection.authenticate();
console.info("Database connection established");
} catch (err) {
console.warn("Error connecting to database: ", err);
throw err;
}
};
const authenticateHealthcheck = async () => {
try {
await SequelizeConnectionHealthcheck.authenticate();
console.info("Database connection for healthcheck established");
} catch (err) {
console.warn("Error connecting to healthcheck database: ", err);
throw err;
}
};
class Database {
static async connectDatabase() {
const maxRetries = 20;
const msBeforeRetry = 1000;
const retry = retryUntilResolved(msBeforeRetry, maxRetries);
try {
await Promise.all([
retry(authenticateDatabase),
retry(authenticateHealthcheck),
]);
console.info('Both connections established');
} catch (error) {
console.error('Could not establish both connections');
}
}
}
Database.connectDatabase();
You should call resolve() in setTimeout().
Please see below example. It is the code that assumes that authenticate() always fails.
async function connectDatabase() {
try {
const retries = 20;
const tryTimeout = async (currentTry, retries) => {
return new Promise((resolve) =>
setTimeout(function () {
console.log(`Try: ${currentTry}, ${retries} retries left.`);
resolve();
}, 1000));
};
for (let i = 1; i <= retries; i++) {
try {
// Establish database connection
await sequelizeConnection_authenticate()
.then(() => {
console.log(
"*** Database connection has been established successfully."
);
})
.catch(async (err) => {
console.log("Error in connect database function: ", err);
throw err;
});
await seqzelizeConnectionHealthcheck_authenticate()
.then(() => {
console.log(
"*** Database connection for healthcheck has been established successfully."
);
})
.catch(async (err) => {
console.log(
"Error in connect database function for healthcheck: ",
err
);
throw err;
});
} catch (error) {
console.log("Error in connectDB retry function");
await tryTimeout(i, retries - i);
}
}
} catch (error) {
console.log("Error in connect database function: ", error);
}
}
async function sequelizeConnection_authenticate() {
return new Promise((_, reject) => {
setTimeout(function () {
reject();
}, 1000);
});
}
async function seqzelizeConnectionHealthcheck_authenticate() {
return new Promise((_, reject) => {
setTimeout(function () {
reject();
}, 1000);
});
}
connectDatabase();
I have the following function that gets a username and by making a query to mssql checks if already exists in databse. I want the checkForUsername() to return true if recordSet.recordset.length >= 1. How can I do that? I am a bit confused about async/await. Thanks.
function checkForUsername(UserIn) {
var dbConn = new sql.ConnectionPool(config);
dbConn.connect().then(function () {
var request = new sql.Request(dbConn);
request.query("SELECT * from Admins WHERE username='"+UserIn+"';").then(function (recordSet) {
console.log(recordSet.recordset.length);
if(recordSet.recordset.length >= 1)
//checkForUsername return true
dbConn.close();
}).catch(function (err) {
dbConn.close();
});
}).catch(function (err) {
console.log(err);
});
}
Of course I can't check if the following code works, but you can try rewriting your code for something like this:
const checkForUsername = async user => {
try {
const
dbConn = new sql.ConnectionPool(config),
sqlObj = await dbConn.connect(),
query = `SELECT * FROM Admins WHERE username='${user}';`, // Note that creating a query explicitly is not recommended as it is vulnerable to SQL injection attack!
recordSet = await new sqlObj.Request(dbConn)query(query),
hasRecords = !!recordSet.recordset.length;
return hasRecords;
} catch (error) {
console.log(error);
} finally {
dbConn.close();
}
}
// Call it
(async () => {
console.log(await checkForUsername('Jhon due');
})();
const checkForUsername = async UserIn => {
try {
var dbConn = new sql.ConnectionPool(config);
const sqlObj = await dbConn.connect();
const recordSet = await new sql.Request(dbConn).query(`SELECT * FROM Admins WHERE username='${UserIn}';`);
const hasRecords = !!recordSet.recordset.length;
return hasRecords;
}catch (error) {
console.log(error);
}finally{
dbConn.close();
}
}
// Call it
(async () => {
console.log(await checkForUsername('John'));
})();
I've got a Lambda function that is triggered by a write to an S3 bucket. It reads the JSON file that is written to the bucket, parses out the individual records, and writes them to a database.
Problem is; I'm not sure what I'm doing wrong, because the stream ends and the Lambda exits before all the data is written.
I'm in "flowing mode" on my readable stream, and I'm pausing/resuming during the db write. According to the docs, this should do the trick, but it's not working as expected.
Lambda handler:
exports.handler = async (event, context) => {
let result = false;
try {
result = await parseData(event);
} catch (e) {
console.error(e);
}
return result;
};
Promise:
const StreamArray = require("stream-json/streamers/StreamArray");
async parseData(event) {
try {
let objectStream = s3.getObject(params).createReadStream();
const streamParser = StreamArray.withParser();
return new Promise((resolve, reject) => {
objectStream.pipe(streamParser).on("data", async streamData => {
objectStream.pause();
let result = await writeData(streamData);
objectStream.resume();
}).on("finish", () => {
console.log("STREAM FINISH!");
resolve(true);
}).on("error", e => {
console.error("Stream error:", e);
reject(e);
});
});
} catch (e) {
console.error(e);
}
}
Got it working by simply swapping-out stream-json with JSONStream, which is a more widely-used package anyhow. Works like a charm now!
const JSONStream = require("JSONStream");
async parseData(event) {
try {
let objectStream = s3.getObject(params).createReadStream();
const streamParser = JSONStream.parse("*");
return new Promise((resolve, reject) => {
objectStream.pipe(streamParser).on("data", async streamData => {
streamParser.pause();
let result = await writeData(streamData);
streamParser.resume();
}).on("finish", () => {
console.log("STREAM FINISH!");
resolve(true);
}).on("error", e => {
console.error("Stream error:", e);
reject(e);
});
});
} catch (e) {
console.error(e);
}
}
So I am running a few insert queries using the mysql npm package and I'm doing so with Promise.all to try and run them at the same time. I have a function that creates a new connection for each query and returns a promise. I am also wrapping the mysql queries with promises. Here is the code that builds the queries and calls them with Promise.all
const Database = require('./database');
const queryBuilder = record =>
new Promise((resolve, reject) => {
try {
const filename = record.s3.object.key.split('/').pop();
const filesize = record.s3.object.size;
const s3path = `${record.s3.bucket.name}/${record.s3.object.key}`;
const createdAt = record.eventTime.split('T').shift();
const sql = 'INSERT INTO raw_files (filename, filesize, s3path, created_at, client_subdomain) ' +
`VALUES ('${filename}', ${filesize}, '${s3path}', '${createdAt}', '${record.s3.bucket.name}')`;
resolve(sql);
} catch (err) {
reject(err);
}
});
const connectionWrapper = (params, record) =>
new Promise((resolve, reject) => {
const db = new Database(params);
db.connect()
.then(res => queryBuilder(record))
.then(sql => db.query(sql))
.then((res) => {
db.close();
resolve(res);
})
.catch((err) => {
db.close();
reject(err);
});
});
exports.handler = (event, context, callback) => {
const connectionParams = {
host: '127.0.0.1',
port: 3306,
user: 'root',
password: 'some_password',
database: 'space_util',
};
Promise.all(event.Records.map(record => connectionWrapper(connectionParams, record)))
.then(res => callback(null, res))
.catch(err => callback(err));
};
And then here is the wrapper module
const mysql = require('mysql');
// Promise-ify the mysql connection
const Database = function Database(connectionParams) {
this.connection = mysql.createConnection(connectionParams);
};
Database.prototype.connect = function connect() {
return new Promise((resolve, reject) => {
this.connection.connect((err) => {
if (err) reject(err);
resolve();
});
});
};
Database.prototype.query = function query(sql) {
return new Promise((resolve, reject) => {
this.connection.query(sql, (err, results, field) => {
if (err) reject(err);
resolve(results);
});
});
};
Database.prototype.close = function close() {
return new Promise((resolve, reject) => {
this.connection.close((err) => {
if (err) reject(err);
resolve('Connection closed');
});
});
};
module.exports = Database;
It works, but I am curious if the way I am doing this seems like a hack or not? Specifically the way I am calling the Promise.all with a map function as the argument. I didn't know how to make an array of connectionWrappers with their params without invoking them, hence the map function in Promise.all(). Any advice would be appreciated!