How to wrap function call inside of Promise - javascript

After exporting the required modules and setting up the variables
let AWS = require("aws-sdk");
const AmazonCognitoIdentity = require('amazon-cognito-identity-js');
const USER_POOL_ID = 'us-east-1_vkXRQuP4U';
const CLIENT_ID = 'mipa4trls0l7323om33mlk80e8';
const poolData = {
UserPoolId : USER_POOL_ID, ClientId : CLIENT_ID
};
const POOL = new AmazonCognitoIdentity.CognitoUserPool(poolData);
let email = "my.email#domain.com";
let password = "My.Password!";
I can go ahead and call signUp command:
POOL.signUp(email, password, [], null, function(err, result) {
console.log('...result:', result);
});
And it works well. Next I want to wrap the POOL.signUp(email, password...) inside async function sign_up like so:
async function sign_up(email, password) {
POOL.signUp(email, password, [], null, function(err, result) {
console.log('...sign_up.result:', result);
return result;
})
};
async function main() {
let signupData = await sign_up(email, password);
console.log('...main.signupData:', signupData);
return signupData;
};
main().then((error, data) => {console.log('...error, data:', error, data)});
While it works fine, the order of the calls that get executed is wrong as the main function doesn't wait for the sign_up() function to complete. In attempt to correct this behavior I wrap the POOL.signUp(email, password...) inside of Promise:
async function sign_up(email, password) {
return await new Promise((resolve) => {
POOL.signUp(email, password, [], null, {
onSuccess: (result) => {
console.log('...result:', result)
return resolve(result);
},
onFailure: (err) => {
return resolve(err.message);
},
});
})
};
But I am getting the error message:
UnhandledPromiseRejectionWarning: TypeError: callback is not a function
Is there a way to avoid this error?

don't need to await the Promise you are returning (since we precisely want our function to be async, we want to defer the waiting to the caller)
Promise constructor function needs to provide the second reject parameter to be able to access the callback in the function implementation
pass a callback function as your POOL.signUp fourth argument, instead of an object
function sign_up(email, password) {
return new Promise((resolve, reject) => {
POOL.signUp(email, password, [], null, function(err, result) {
if (err) {
return reject(err.message);
}
console.log('...result:', result)
resolve(result);
});
})
};

Related

How do I return a value for a non promise callback inside an async fucntion

In below Node.js function, how do I return data.QueueUrl, so that a caller can get the value with
var url = await createSubscription('foo', req);
This is the function
async function createSubscription(name, req){
var params = {
QueueName: name,
Attributes: {
'ReceiveMessageWaitTimeSeconds': '20', // long polling wait time
}
};
sqs.createQueue(params, function(err, data) {
if (err) {
logger.error("createQueue error : " + err, req);
} else {
logger.log("queue " + name + ' created', req);
return data.QueueUrl;
}
});
}
Best practice is to wrap problematic functions at the lowest possible level, and then never call them directly again - MDN
For you this means doing the following:
function createQueue(params) {
return new Promise((resolve, reject) => {
sqs.createQueue(params, (err, data) => err ? reject(err) : resolve(data));
});
}
After that you can work with createQueue instead of sqs.createQueue:
async function createSubscription(name, req) {
const params = {
QueueName: name,
Attributes: {
'ReceiveMessageWaitTimeSeconds': '20', // long polling wait time
}
};
try {
const data = await createQueue(params);
logger.log("queue " + name + ' created', req);
return data.QueueUrl;
} catch (err) {
logger.error("createQueue error : " + err, req);
// you'll probably want to re-throw here unless the caller knows
// that an undefined return value means an error has occurred
}
}
Without all the additional log statements, the function can also look like this:
async function createSubscription(name, req) {
const params = {
QueueName: name,
Attributes: {
'ReceiveMessageWaitTimeSeconds': '20', // long polling wait time
}
};
const data = await createQueue(params);
return data.QueueUrl;
}
Since we don't use a try/catch the error/rejected promise bubbles up to the caller.
Since all the SQS functions are in the style sqs.function(params, callback) (see documentation), you could also write a more general helper that works on all the SQS functions.
function sqsPromise(sqsFnName, params) {
return new Promise((resolve, reject) => {
sqs[sqsFnName](params, (err, data) => err ? reject(err) : resolve(data));
});
}
Which allows you to do:
const data = await sqsPromise("createQueue", params);
The function doesn't need to be async to create and return a promise:
function createSubscription (name, req) {
return new Promise((resolve, reject) => {
const params = {
QueueName: name,
Attributes: {'ReceiveMessageWaitTimeSeconds': '20'}
};
sqs.createQueue(params, (err, data) => {
if (err) {
logger.error('createQueue error : ' + err, req);
reject(err);
} else {
logger.log('queue ' + name + ' created', req);
resolve(data.QueueUrl);
}
});
});
}
const url = await createSubscription('foo', req);
You could make your function to return a Promise and resolve and reject the required data according to your logic. The other way is to promisfy your callback using the util module.
Write your function like this to return a promise
async function createSubscription(name, req){
try{
const params = {
QueueName: name,
Attributes: {
'ReceiveMessageWaitTimeSeconds': '20', // long polling wait time
}
};
let data = await sqs.createQueue(params).promise();
return data;
}
catch(err){
logger.error("createQueue error : " + err.message);
}
}
And call it like this to receive the value
const url = await createSubscription('foo', req);
console.log(url.QueueUrl); // You'll get the result in url variable.

TypeError: query.findAll is not a function node js mysql

i am trying to pass the sql query parameters to data access js file. i have imported the function in current file, but still am getting the below error.
current file
const tcount = async (value) => {
const sql = 'trainingcount';
const result = await query.findAll(sql);
return result;
}
data access file
const query = (results) => {
findAll: async (sql, result) => {
connection.query(`SELECT * FROM trainingcount`, (err, rows) => {
if (err) {
return results(null, err);
} else {
return results(rows);
}
});
};
};
export { query };
(node:11132) UnhandledPromiseRejectionWarning: TypeError:
query.findAll is not a function
EDIT: Check #rid solutions on the comments for the specific problem of calling the proper function. My answer solves a different problem in OP code.
you call return inside the callback function, so you are returning THAT function, not findAll. You need to return a Promise:
const query = (results) => {
findAll: (sql, result) => {
return new Promise((resolve, reject) => {
connection.query(`SELECT * FROM trainingcount`, (err, rows) => {
if (err) {
reject(err);
} else {
resolve(rows);
}
});
});
};
};
export { query };

How to use callback in .then() function in Nodejs?

I have nodejs module to fetch data from mongodb database using mongodb driver. Callback is passed to given function which return a promise, but instead of returning result in .then() function, it is passing value to callback function. How can I call this function from other module or function since it is not returning it in .then()? I tried to console the result of .then(), but it is showing undefined.
const MongoClient = require('mongodb').MongoClient;
const Db = require('../model/db');
Db.findUser = (details, callback) => {
return dbconnection().then(db => {
if (db) {
return db.collection('users').findOne({
email: details.email,
pass: details.password
}).then(data => {
if (data) {
console.log('Found one');
callback(true);
} else {
let err = new Error();
callback(err);
}
})
}
I have used following function to call the promise. I am new to promises.
var getUser = function(callback) {
db.findUser().then(result => {
console.log(result) // undefined
})
}
You can easily do it using async/await. Something like this:
Db.findUser = async (details, callback) => {
const db = await dbconnection();
const data = await db.collection('users').findOne({
email: details.email,
pass: details.password
});
if (data) {
console.log('Found one');
callback(true);
} else {
let err = new Error();
callback(err);
}
return data;
}
and consume it like:
const getUser = async (details, callback) => {
const data = await Db.findUser();
// do whatever you need with data
return data;
}

Make the return statement wait until everything else in the function is finished

I'm trying to make a function that returns the results of a SOAP call (using npm-soap in combination with node.js). The problem is that the function returns undefined because the SOAP call isn't finished yet when the return statement is reached.
I tried putting the return statement in the SOAP call callback itself, but then it returns undefined. I think this is because the return statement should be in the outer function instead of the inner function, just like I did in the example below. A console.log() in the SOAP call callback outputs the right data, so I know it's there.
How do I make the return statement wait on the inner SOAP call? Thanks!
var config = require('./config.js');
var soap = require('soap');
function getInvoices() {
let invoices;
// Connect to M1
soap.createClient(config.endpoint, function(err, client) {
// Log in
client.login(
{
username: config.username,
apiKey: config.password
},
function(err, loginResult) {
// Get invoices
client.salesOrderInvoiceList(
{
sessionId: loginResult.loginReturn.$value
},
function(err, invoiceResult) {
// Save invoices
invoices = invoiceResult;
console.log(invoices); // <- Returns the right data
// Log out
client.endSession(
{
sessionId: loginResult.loginReturn.$value
},
function(err, logoutResult) {
}
);
}
);
});
});
// Return invoices
return invoices; // <- Returns undefined
}
console.log(getInvoices(); // <- So this returns undefined as well
Have getInvoices return a Promise which you can then resolve once all the callbacks finish i.e.
function getInvoices() {
return new Promise((resolve, reject) => {
// Connect to M1
soap.createClient(config.endpoint, (err, client) => {
if (err) return reject(err);
// Log in
client.login({
username: config.username,
apiKey: config.password
}, (err, loginResult) => {
if (err) return reject(err);
// Get invoices
client.salesOrderInvoiceList({
sessionId: loginResult.loginReturn.$value
}, (err, invoiceResult) => {
if (err) return reject(err);
// Log out & resolve the Promise
client.endSession({
sessionId: loginResult.loginReturn.$value
}, (err, logoutResult) =>
err ? reject(err) : resolve(invoiceResult)
);
});
});
});
}
...
(async () => {
try {
const invoices = await getInvoices();
console.log(invoices);
} catch (e) {
console.error(e);
}
})();

Correctly chaining Promises

I want to bind promises sequentially, inside a loop. I need this to user accounts, where the result of one operation depends on another.
I am trying to write a flat version - all code in one place, using bind. That's at least what I wanted. I wrapped promises around two create methods, as below:
function create(myApi, record) {
return new Promise(function (resolve, reject) {
myApi.create(record, function (err, result) {
if (err) reject(err);
else resolve(result);
});
});
}
function createUser(myApi, record) {
return new Promise(function (resolve, reject) {
myApi.createUser(record, function (err, result) {
if (err) reject(err);
else resolve(result);
});
});
}
Now, I want to create users in a loop as:
for ( var i = 0; i < dummyData.accounts.length; i++) {
var cursorUser = dummyData.accounts[i];
var auth0User = {
email: cursorUser.email,
password: cursorUser.password,
connection: 'Username-Password-Authentication'
};
createUser(api, auth0User)
.then( function(auth0Info) {
console.log("Auth0 userInfo: ", auth0Info);
cursorUser.authProfile = auth0Info;
create(accountsAPIService, cursorUser)
.then(function (account) {
console.log("created account:", account);
})
.catch(function (err) {
console.log('count not create account for user, error: ', err, '\nfor: ', auth0User);
});
})
.catch(function (err) {
console.log('could not create auth0 user, error: ', err, '\nfor: ', auth0User);
});
}
Since the two method are asynchronous, it is of course not working correctly. Calls are not executed sequentially. I want to chain promises so that create does not run until a call from createUser returned. Tried using bind, but it did not work for me. It is how one should do the sequential chaining? I bind on .then of the createUser? Please advise.
When you return a promise from a then, the then chained after will resolve/reject with that promise instead of the original promise.
createUser(api, auth0User).then(function(auth0Info) {
cursorUser.authProfile = auth0Info;
// Return create's promise
return create(accountsAPIService, cursorUser);
}, function (err) {
console.log('could not create auth0 user, error: ', err, '\nfor: ', auth0User);
})
// This will wait for create's promise instead of createUser's promise
.then(function (account) {
console.log("created account:", account);
}, function (err) {
console.log('count not create account for user, error: ', err, '\nfor: ', auth0User);
})
Using ES6 you can use generators which allows you to do write async task as they were async. In this example i am using bluebird but ofc there are others great valid options.
var CreateUserGenerator = BPromise.coroutine(function * (arg) {
for ( var i = 0; i < dummyData.accounts.length; i++) {
var cursorUser = dummyData.accounts[i];
var auth0User = {
email: cursorUser.email,
password: cursorUser.password,
connection: 'Username-Password-Authentication'
};
var auth0Info = yield createUser(api, auth0User);
console.log("Auth0 userInfo: ", auth0Info);
cursorUser.authProfile = auth0Info;
var account = yield create(accountsAPIService, cursorUser)
console.log("created account:", account);
}
}
function generateorWithCatch (argument) {
creatLoopUser(arg)
.catch(function (err) {
console.log('could not create auth0 user, error: ', err, '\nfor: ', auth0User);
});
}
In this solution i assume your create and createUser functions are written and return value correctly

Categories