How can I wrap an async call in Javascript? - javascript

I want to have three functions that do something like this:
function getuser1(){
var user = b();
//do some stuff to user
return user;
}
function getuser2(){
var user = asyncfinduser();
//do some different stuff to user
return user;
}
function asyncfinduser(){
userDB.find( { /* some criteria */ }, function (error, user) {
//return found user to functions above
return user;
});
)
The above is obviously not going to work so how can I fix this? Thank you

Since you cannot return synchronously from async callbacks, you will need to use callbacks:
function getuser1(callback) {
asyncfinduser(function(error, user) {
if (error) return callback(error);
//do some stuff to user
callback(null, user);
});
}
function getuser2(callback) {
asyncfinduser(function(error, user) {
if (error) return callback(error);
//do some different stuff to user
callback(null, user);
});
}
function asyncfinduser(callback) {
userDB.find( { /* some criteria */ }, function (error, user) {
if (error) return callback(error);
//return found user to functions above
callback(null, user);
});
}
However, you might be able to apply the promise pattern:
var Promise = …; // some library
function getuser1() {
return asyncfinduser().then(function(user) {
//do some stuff to user
return user;
});
}
function getuser2() {
return asyncfinduser().then(function(user) {
//do some different stuff to user
return user;
});
}
function asyncfinduser() {
return new Promise(function(resolve, reject) {
userDB.find( { /* some criteria */ }, function (error, user) {
if (error) reject(error);
else resolve(user); //return found user to functions above
});
}

Related

Mysql node function

I want to make a call to the mysql server with node and store data into a fucntion:
async function getData(uri){
let data = await esisteMysql(uri);
console.log(data);
}
function esisteMysql(uri) {
connection.query(`select * from data where url = '${uri}'`, function (error, row) {
if(error) throw error;
else {
if(row && row.length){
console.log('FOUND');
//console.log(row);
return row;
}else{
console.log('NOT FOUND');
return row = null;
}
}
})
}
It doesn't work, it returns an undefined. I can log data only if I print it in esisteMysql with console.log(row);
Your esisteMysql() needs to produce a promise in order to work with await. It might look something like:
async function getData(uri) {
let data = await esisteMysql(uri);
console.log(data);
}
function esisteMysql(uri) {
return new Promise(function(resolve, reject) {
connection.query(`select * from data where url = '${uri}'`, function (error, row) {
if (error) {
reject(error);
} else {
resolve(row);
}
})
}
}
See https://javascript.info/async for more.

how to run mongoose method inside for loop, as mongoose function is asynchronous

I am trying to run an mongoose find command and if it matches few dataset in array, then need to update the database.
here is the query i have written.
membersId=["U-ZCIwZGvW", "U-MbyAVxXf", "U-Gbe9RhGu"];
let updateUserData=(groupData)=>{
return new Promise((resolve,reject)=>{
for(M in membersId){
console.log(membersId[M]);
UserModel.findOne({userId:membersId[M]},(err,response)=>{
if(err){
console.log(err);
reject(err);
}else if(check.isEmpty(response)){
reject("Id not found");
}else{
if(!response.groups.includes(groupData._id)){
response.groups.push(groupData._id)
response.save((err,data)=>{
if(err){
console.log(err);
reject(err);
}else{
console.log(data);
}
})
}
}
})
}
resolve(groupData,'Members Added','AddMembersToGroup',00);
})
}
i read about async-await and tried this..
membersId=["U-ZCIwZGvW", "U-MbyAVxXf", "U-Gbe9RhGu"];
let updateUserData = (groupData) => {
return new Promise((resolve, reject) => {
async function getTodos() {
for (const M of membersId) {
await UserModel.findOne({ userId: M }, (err, response) => {
if (err) {
console.log(err);
reject(err);
} else if (check.isEmpty(response)) {
reject("Id not found");
} else {
if (!response.groups.includes(groupData._id)) {
response.groups.push(groupData._id)
response.save((err, data) => {
if (err) {
console.log(err);
reject(err);
} else {
console.log(data);
}
})
}
}
})
}
console.log('Finished!');
resolve(groupData,'Members Added','AddMembersToGroup',00);
}
getTodos();
})
}
the async method is working, but still it is not fully synchronous, and also if any error occurs, it does not stop.
how to exit the for loop on error and do the resolve statement only once so that it does not go back to other code once run.
even if there is error it runs.
There are a few things :
you don't need to perform multiple find requests to find & update multiple records, just use updateMany with the right filter & update parameters
an async function return a Promise which is not the case of your function getTodos
you need to invoke the async function like this : await getTodos();
in case your promise rejects something you need to handle this in try/catch (error is thrown)
You can use the following inside an async function :
try {
var response = await getTodos(groupData, membersId);
console.log(response);
}
catch(err){
console.log(err);
}
with the getTodos function :
async function getTodos(groupData,membersId) {
return new Promise(function(resolve, reject){
UserModel.updateMany({
userId: { "$in" : membersId},
groups: { "$ne": groupData._id }
},{
$push: { groups: groupData._id }
}, function(err,response){
if (err) {
reject(err);
} else if (response.nModified === 0){
reject("Id not found");
} else {
resolve(response.nModified + " items modified");
}
});
});
}

AngularJS - Multiple calls to the same service in a function

Is it a bad code design if I put multiple calls to the same service in a single function? Each call to the service returns a value. Are there other better ways of implementing this?
For example:
$scope.getAdditionalInfo = () => {
ExampleService
.methodName($scope.parameter)
.then(function (res) {
//do things here
}, function (err) {
alert(err.message);
console.log(err);
});
ExampleService
.methodName2($scope.parameter)
.then(function (res) {
//do things here
}, function (err) {
alert(err.message);
console.log(err);
});
ExampleService
.methodName3($scope.parameter)
.then(function (res) {
//do things here
}, function (err) {
alert(err.message);
console.log(err);
});
}

Javascript promises: how to deal with token expiry in API calls?

When I am receiving an error from my API that my token has expired I want to be able to re-issue a new token and repeat the request as first sent without the user feeling any difference. Is there a way to handle this use case elegantly?
function relogin(userInfo) {
return Q.Promise(function (resolve, reject) {
// ...
});
}
function makeTransaction(token, options) {
dataOperations.makeTransaction(token, options).then(function (result) {
// notify ui - new data yey!
}).catch(function (err) {
if (err.code === 304) {
relogin.then(function (token) {
makeTransaction(token, options);
}).catch(function (err) {
// notify UI - problems with server
});
}
});
}
that code looks pretty good already. what specifically is your issue? If you have many transactions and you're worried about code duplication, and checking for HTTP 304 many times, you could wrap your transactions in a handler:
function relogin(userInfo) {
return Q.Promise(function(resolve, reject) {
// ...
});
}
function myTask(params) {
return Q.Promise(function(resolve, reject) {
// ...
});
}
function doTask(fn, context, params) {
return fn.apply(context, params).then(function(results) {
return results;
}).catch(function(err) {
if (err.code === 304) {
return relogin({}).then(doTask(fn, context, params));
}
else {
return err;
}
});
}
// ...
$("#foo").on("click", function(e) {
doTask(myTask, this, params).done(function(results) {
alert("all done");
}).catch(function(err) {
alert(err);
});
});

node.js chain multiple promises (with mongoose)

The following is a typical promise function that I am dealing with.
var _delete = function(t, id) {
return Promise.cast(Event.find({where: {id: id}}, {transaction: t}))
.then(function(d){
if (d) {
// ------- (*)
return Promise.cast(d.updateAttributes({status: -1}, {transaction: t}))
.then(function(){
// do inventory stuff
return Promise.cast(Inventory.update({}).exec())
.then(function(d){
// do something
})
}).then(function(){
// do product stuff
return Promise.cast(Product.update({}).exec())
.then(function(d){
// do something
})
})
} else {
return Promise.reject('this transaction list does not exist');
}
});
};
This looks ok until when I am dealing with more complicated update / creates the code will become really messy.
Currently what I am doing with promise is that
1. I have a lot of useless return true statements and the only purpose is to go to next .then statement
2. promise are programmed in a nested style. also the input arguments are usually complicated and has more then 1 arguments so that I cannot do something like this
.then(fun1).then(fun2)
... etc
which makes me unable to 'tap' the .then statement to enable/disable a functionality.
So my questions is how do I do this correctly? Thanks..
the following is the really ugly things that I am talking about....
var _process = function(t, tid) {
var that = this;
return Promise.cast(Usermain.find({where: {transaction_id: tid}}))
.bind({}) // --- (*)
.then(function(d){
this.tmain = d;
return true; // ---- do nothing, just go to next thennable (is this correct)
}).then(function(){
return Promise.cast(Userlist.findAndCountAll({where: {transaction_id: tid}}))
}).then(function(d){
this.tlist = d;
return true; // ---- do nothing, just go to next thennable (is this correct)
}).then(function(){
if (this.tmain.is_processed) {
return Promise.reject('something is wrong');
}
if (this.tlist.count !== this.tmain.num_of_tran) {
return Promise.reject('wrong');
}
return Promise.resolve(JSON.parse(JSON.stringify(this.tlist.rows)))
.map(function(d){
if (d.is_processed) return Promise.reject('something is wrong with tran list');
return true; // goto next then
});
}).then(function(){
return Promise.cast(this.tmain.updateAttributes({is_processed: 1}, {transaction: t}));
}).then(function(){
return Promise.resolve(this.tlist.rows)
.map(function(d){
var tranlist = JSON.parse(JSON.stringify(d));
return Promise.cast(d.updateAttributes({is_processed: 1, date_processed: Date.now()}, {transaction: t}))
.then(function(d){
if (!d) {
return Promise.reject('cannot update tran main somehow');
} else {
if (tranlist.amount < 0) {
return Usermoney._payBalance(t, tranlist.user_id, -tranlist.amount);
} else {
return Usermoney._receiveBalance(t, tranlist.user_id, tranlist.amount);
}
}
});
});
});
}
You can do two things:
Unnest then callbacks
Modularize. These "do product stuff" and "do inventory stuff" things might become their own functions (or even the same?).
In this case, unnesting could do the following (assuming you don't need closures in your commented sections):
function _delete(t, id) {
return Promise.cast(Event.find({where: {id: id}}, {transaction: t}))
.then(function(d){
if (d) {
return Promise.cast(d.updateAttributes({status: -1}, {transaction: t}));
else
throw new Error('this transaction list does not exist');
})
.then(function(){
// do inventory stuff
return Promise.cast(Inventory.update({}).exec())
})
.then(function(d){
// do something
})
.then(function(){
// do product stuff
return Promise.cast(Product.update({}).exec())
})
.then(function(d){
// do something
});
}
In my projects I use Async.js
I think you need to decompose your _process method into small actions
Actions which depend on result from previous actions - async waterfall pattern might be used here
Actions which don't depend on the previous actions result, they may be executed in parallel
Use some custom process
Here is an example from my app:
async.waterfall([
function findUser(next) {
Users.findById(userId, function (err, user){
if(err) {
next(new Error(util.format('User [%s] was not found.', userId)));
return;
}
next(null, user);
});
},
function findUserStoriesAndSurveys(user, next) {
async.parallel([
function findStories(callback) {
// find all user stories
Stories.find({ UserGroups: { $in : user.Groups } })
.populate('Topic')
.populate('Episodes')
.exec(function(err, stories) {
if(err) {
callback(err);
return;
}
callback(null, stories);
});
},
function findSurveys(callback) {
// find all completed surveys
Surveys.find({
User: user
}).exec(function(err, surveys) {
if(err) {
callback(err);
return;
}
callback(null, surveys);
});
}
],
function(err, results) {
if(err) {
next(err);
return;
}
next(null, results[0], results[1]);
});
},
function calculateResult(stories, surveys, next) {
// do sth with stories and surveys
next(null, { /* result object */ });
}
], function (err, resultObject) {
if (err) {
res.render('error_template', {
status: 500,
message: 'Oops! Server error! Please reload the page.'
});
}
res.send(/* .... */);
});
Please refer to Async docs for a custom process, it really does contain a lot of common patterns, I also use this library in my client JavaScript.

Categories