Correctly chaining Promises - javascript

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

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.

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);
}
})();

JavaScript promise not firing in order

I have been trying to effectively manage how I build my promises in my Express.js app.
Right now I have the following scenario: During the signup process, a user has an optional Organization Name field. If this is filled in, I need to create the Organization object and then add the _id of it to the other information that I am applying to the user. If there is no Organization name, proceed and update the basic user info.
<-- Right now the user is being updated before the organization is being created. -->
//basic info passed from the signup form
info['first_name'] = req.body.first_name;
info['last_name'] = req.body.last_name;
info['flags.new_user'] = false;
//if organization name is passed, create object and store _id in user object.
let create_organization = new Promise(function(resolve, reject) {
if (req.body.organization_name !== "") { //check if name is sent from form
Organization.create({
name: req.body.organization_name
}, function(err, result) {
console.log(result);
if (!err) {
info['local.organization'] = result._id;
resolve()
} else {
reject()
}
})
} else {
resolve()
}
});
let update_user = new Promise(function(resolve, reject) {
User.update({
_id: req.user._id
}, info, function(err, result) {
if (!err) {
console.log("Updated User!"); < --prints before Organization is created
resolve();
} else {
reject();
}
})
});
create_organization
.then(function() {
return update_user;
})
.then(function() {
res.redirect('/dash');
})
Nothing in your code waits for the first promise to settle before proceeding with starting the subsequent work. The work is started as soon as you call User.update, which is done synchronously when you call new Promise with that code in the promise executor.
Instead, wait to do that until the previous promise resolves. I'd do it by wrapping those functions in reusable promise-enabled wrappers (createOrganization and updateUser):
// Reusable promise-enabled wrappers
function createOrganization(name) {
return new Promise(function(resolve, reject) {
Organization.create({name: name}, function(err, result) {
console.log(result);
if (err) {
reject(err);
} else {
resolve(result);
}
});
});
}
function updateUser(id, info) {
return new Promise(function(resolve, reject) {
User.update({_id: id}, info, function(err, result) {
if (err) {
reject(err);
} else {
resolve();
}
})
});
}
(You may be able to use util.promisify or the promisify npm module to avoid doing that manually.)
And then:
//basic info passed from the signup form
info['first_name'] = req.body.first_name;
info['last_name'] = req.body.last_name;
info['flags.new_user'] = false;
//if organization name is passed, create object and store _id in user object.
(req.body.organization_name === "" ? Promise.resolve() : createOrganization(req.body.organization_name))
.then(function() {
return updateUser(req.user._id, info);
})
.catch(function(error) {
// handle/report error
});
(I stuck to ES5-level syntax since your code seemed to be doing so...)
See, so called 'executor function', passed into Promise constructor, is invoked immediately. That's why you essentially have a race condition here between two remote procedure calls. To solve this, make the update responsible for promise creation instead:
function updateUser(userId, userInfo) {
return new Promise(function(resolve, reject) {
User.update({_id: userId}, userInfo, function(err, result) {
if (err) {
reject(err);
}
else {
resolve(result);
}
});
});
}
... and call this function in then(). By doing this, an executor function will be called only when updateUser is invoked - and that'll be after createOrganization() finished its job.

javascript promise all array of values passed as argument

Given the following array of values:
var sportList = ['football', 'volleyball'];
i want to run a query on mongo database using each of these values:
function myFunc(sport, callback) {
mongoDB.sports.find({'name': sport}, function (error, result) {
if (error) {
callback(error)
} else {
callback(null, result)
}
})
}
so i build my promises like:
var promises = sportList.map(function(val){
return myFunc(val);
});
and then trying to run all in a promise all chain:
Promise.all(promises)
.then(function (result) {
console.log('log results: ', result);
})
.catch(function (error) {
console.log(error);
});
but this is not working, because it is complaining that the callback is undefined, how can i fix this up correctly?
The reason for the error is that you are calling the myFunc method without supplying the callback parameter.
A solution would be to replace the myFunc function with the below. This function will return a new Promise. I haven't tested the below code but it should work.
function myFunc(sport) {
return new Promise((resolve, reject) => {
mongoDB.sports.find({'name': sport}, function (error, result) {
if (error) {
reject(error);
} else {
resolve(result);
}
})
}));
}

Node.js - How to chain Promise in The right way

I recently moved from callback functions to promises in node.js. I want to preform async query to the DB (psql) in the most elegant way. I was wondering if the following code is the right way to do it or if I can chain for example the promises in a way of first().then(second).then(third).
function queryAll(req, res) {
searchQuery()
.then((rows) => {
console.log(rows);
totalQuery()
.then((total) => {
console.log(total);
});
});
res.json({"rows": rows, "total": total});
}
function searchQuery() {
return new Promise(function(resolve, reject) {
var rowData = { rows: {} };
pool.query('select age, sex from workers;', values, function(err, result) {
if(err) {
return console.error('error running query', err);
reject(err);
}
rowData.rows = result.rows;
resolve(rowData);
});
});
}
function totalQuery() {
return new Promise(function(reject, resolve) {
var totalData = { totals: {} };
pool.query('select sex, scores from workers group by sex;', values, function(err, result) {
if(err) {
return console.error('error running query', err);
reject(err);
}
totalData.totals = result.rows;
resolve(totalData);
});
});
}
var rowData = { rows: {} };
var totalData = { totals: {} };
First of all, these make no sense stored in variables since there's nothing else on the object. Just resolve with the rows directly instead.
return console.error('error running query', err);
Also, don't just console.log your errors. then accepts a second callback that executes on thrown errors or rejected promises. Throw this message in an error or reject with it instead. Also, I would leave logging to the consumer.
function queryAll(req, res) {
searchQuery()
.then((search) => {
console.log(rows);
totalQuery()
.then((total) => {
console.log(total);
});
});
res.json({"rows": rows, "total": total});
}
rows and total don't exist anywhere. Plus, by the time res.json executes, rows and total (assuming they come from inside the callbacks) won't exist yet since the whole sequence is async. You'll get undefined values as results.
I see little point in running searchQuery and totalQuery in sequence as they're not dependent on each other. It's better to run them parallel instead. Use Promise.all for that.
function queryAll(req, res) {
Promise.all([
searchQuery(),
totalQuery()
]).then(values => {
const rows = values[0];
const total = values[1];
res.json({"rows": rows, "total": total});
}, function(e){
// something went wrong
});
}
function searchQuery() {
return new Promise(function(resolve, reject) {
pool.query('select age, sex from workers;', values, function(err, result) {
if(err) reject(err);
else resolve(result.rows);
});
});
}
function totalQuery() {
return new Promise(function(reject, resolve) {
pool.query('select sex, scores from workers group by sex;', values, function(err, result) {
if(err) reject(err);
else resolve(result.rows);
});
});
}
You have a few issues in the code:
You return before executing reject()
There is an undefined rows variable (mismatch with search)
res.json is executed before the results are in.
The promises resolve to objects like { rows: rows }, but the main function seems to expect the plain numbers, not the objects. So let the promises just resolve to numeric values.
The second SQL is ambiguous since the second field is not aggregated and does not appear in the group by clause either. Assuming you want to sum the scores, use sum().
The second query is only launched after the first one has returned results, but this can be done in parallel
You have very similar code repeated. Try to reuse code and make the SQL statement an argument.
Here is how I would suggest to do it:
function queryAll(req, res) {
return Promise.all([searchQuery(), totalQuery()]).then(([rows, total]) => {
console.log('rows', rows);
console.log('total', total);
// Make sure to only access the promised values in the `then` callback
res.json({rows, total});
});
}
function searchQuery() {
return promiseQuery('select age, sex from workers;');
}
function totalQuery() {
// When you group, make sure to aggregate:
return promiseQuery('select sex, sum(scores) as scores from workers group by sex;');
}
function promiseQuery(sql) { // reusable for other SQL queries
return new Promise(function(resolve, reject) {
pool.query(sql, values, function(err, result) {
if(err) {
// Do not return before calling reject!
console.error('error running query', err);
reject(err);
return;
}
// No need for a variable or object, just resolve with the number of rows
resolve(result.rows);
});
});
}
The most elegant solution would be via pg-promise:
function queryAll(req, res) {
db.task(t => {
return t.batch([
t.any('SELECT age, sex FROM workers', values),
t.any('SELECT sex, scores FROM workers GROUP BY sex', values)
]);
})
.then(data => {
res.json({rows: data[0], total: data[1]});
})
.catch(error => {
// handle error
});
}
And that's everything. You don't have to reinvent promise patterns for working with the database, they are all part of the library already.
And if your queries have a dependency, see: How to get results from multiple queries at once.
Or if you prefer ES6 generators:
function queryAll(req, res) {
db.task(function* (t) {
let rows = yield t.any('SELECT age, sex FROM workers', values);
let total = yield t.any('SELECT sex, scores FROM workers GROUP BY sex', values);
return {rows, total};
})
.then(data => {
res.json(data);
})
.catch(error => {
// handle error
});
}
And with the ES7's await/async it would be almost the same.
First of all there are some errors in your code, you have to place the reject before the return, otherwise it will be never called, and create a dangling promise:
function searchQuery() {
return new Promise(function(resolve, reject) {
var rowData = {
rows: {}
};
pool.query('select age, sex from workers;', values, function(err, result) {
if (err) {
reject(err);
console.error('error running query', err);
} else {
rowData.rows = result.rows;
resolve(rowData);
}
});
});
}
Beside that you should not nest the Promises when ever possible.
So it should be:
function queryAll(req, res) {
var result = {};
searchQuery()
.then((search) => {
console.log(search);
result.rows = search;
return totalQuery();
})
.then((total) => {
result.total = total;
console.log(total);
});
}
The res.json has to be called in the then part of the Promise:
function queryAll(req, res) {
var result = {};
searchQuery()
.then((search) => {
console.log(search);
result.rows = search;
return totalQuery();
})
.then((total) => {
result.total = total;
console.log(total);
})
.then(() => {
res.json({
"rows": result.rows,
"total": result.total
});
});
}
If your queryAll is called as e.g. middleware of express, then you should handle the catch case within queryAll:
function queryAll(req, res) {
var result = {};
searchQuery()
.then((search) => {
console.log(search);
result.rows = search;
return totalQuery();
})
.then((total) => {
result.total = total;
console.log(total);
})
.then(() => {
res.json({
"rows": result.rows,
"total": result.total
});
})
.catch( err => {
res.status(500).json({error: 'some error'})
});
}
For postgress I would suggest to use pg-promise instead of using a callback style library and wrapping it into promises yourself.
You could simplify the code if you use a library like bluebird:
const bPromise = require('bluebird')
function queryAll(req, res) {
bPromise.all([
searchQuery(),
totalQuery()
])
.spread((rows, total) => {
res.json({
"rows": rows,
"total": total
});
})
.catch(err => {
res.status(500).json({
error: 'some error'
})
});
}
With nsynjs your logic may be coded as simple as this:
var resp = {
rows: dbQuery(nsynjsCtx, conn, 'select age, sex from workers', values1).data,
total: dbQuery(nsynjsCtx, conn, 'select sex, scores from workers group by sex', values2).data
};
Please see example of multiple sequential queries here: https://github.com/amaksr/nsynjs/tree/master/examples/node-mysql

Categories