so, i was using passport-jwt and come on to this problem. i have two models that are Employee with name, address, role, ... and Employee credential with username, passsword , employee(Object_Id).
Then what i wanted is that after i found the employee username and password then i need the employee role so i used employee.findbyId but it returns a promise and call the callback done before getting role of the employee.
what i want is why it is happening(detail about it for understanding)??? i didn't use async/await so the function is synchronous.
(passport)=>{
let options = {};
options.jwtFromRequest = ExtractJwt.fromAuthHeaderWithScheme('jwt');
options.secretOrKey = config.SECRET;
passport.use(new Strategy( options,(payload,done)=>{
Employee_credential.findById(payload.data._id, (err, employee)=>{
if(err) return done(err,false);
if(employee){
var result = Employee.getById(employee.employee).populate('role').catch(err=>{throw err})
employee.role = result.role.role_Id;
return done(null, employee);
}
return done(null,false);
})
}))
}
Things may be happening sequentially here, but not synchronously. Your functions are happening in the order as written, but it is not going to wait on the result of your API call. That is why you are getting a promise instead of the role of the employee. To make this work you should either use async/await (my preferred method) or return a promise and set up the next function with .then.
A note from personal experience, not all APIs are going to work properly with await depending on how they handle promises. In some cases, I've had to combine that with explicitly resolving the promise via return new Promise((resolve) => { //your function; resolve(employee); } or something like that.
Related
I've created a code snippet here that fetches data about a certain country using REST Countries API. The function works fine, and returns a pending promise. That promise's fulfilled value will equal an object containing key value pairs for the country's capital, name, and code.
But if I wanted to use them for something, why wouldn't I just set that very same object equal to a variable and continue to program new actions inside my async function? Why bother trying to use values gained asynchronously on the global scope?
function getJSON(url, errorMSG = 'Something went wrong') {
// feed it the fetch URL, then your customized error message
return fetch(url).then(response => {
if (!response.ok) throw new Error(errorMSG);
return response.json();
});
}
async function countryData(nation) {
try {
const info = await getJSON(
`https://restcountries.eu/rest/v2/name/${nation}?fullText=true`,
'Invalid country selected'
);
return {
// Fullfilled value of promise
capital: info[0].capital,
name: info[0].name,
code: info[0].cioc,
};
} catch (err) {
console.error(err); // display your custom error message
}
}
console.log(countryData('Canada'));
fetch is an async function. Why do they resolve the promise to a response object, instead of continuing to run extra actions once they have it? Because they don't know what you want to do with it. It would be impossible for fetch to handle every possible thing that should happen next, so it just has a single job: get the data, then return it (in a promise). You can then combine this with whatever other code you like.
On a smaller scale, this may happen with countryData too. You might have 10 different parts of your app that want to do things with the result from countryData. It may not be "impossible" for countryData to implement all 10 things, but it's definitely impractical and not a good idea. Instead, countryData can be written to have one job: get the country data and return it. Then each of the 10 pieces of code can do their own things with the result.
This isn't about it being async, the same principles apply to synchronous code to. If you can keep code focused on a single task, without entangling it with the needs of other pieces of code, then your code becomes easier to maintain.
So I have code that looks like this in my controller.
const Users = require("../models/users");
class UserController() {
getUserById(req, res) {
const id = req.params.id;
const users = new Users();
const userData = users.getUserById(uid).then(function(result) {
//Do other logic here that depends on db result
return res.status(200).json({status:"success", user: result});
});
}
}
My question is, what's the difference between that and the following, and which is the better practice?
const Users = require("../models/users");
class UserController() {
async getUserById(req, res) {
const id = req.params.id;
const users = new Users();
const userData = await users.getUserById(uid);
return res.status(200).json({status:"success",user:userData});
}
}
The second function call is using async-await feature of ES8, This makes our code appear as if it is synchronous and clean also, without doing .then() and callback within it. Both fetch() and async-await work on promise though.
Aync-Await is much more readable if I can put it this way. No call backs used here. It simply waits for the first API call to happen and only when the data arrives (or Promise is resolved) we fire another API call. For programmer itself life becomes little easy with Async-Await syntax.
For Error handling you will need try{} catch{} block in Aync-await but in case of fetch(), .catch() block handles API Errors for us.
I use pg-promise and i tried to make a request to database in user model to get an array of all users like this:
exports.getAllUsers = function () {
let users = db.any("SELECT * FROM users;").then(function (data) {
return data; // in debug mode data is my expected array
});
return users;
};
But when i make get request from controller, it returns promise Promise { <pending> }, not an array of json objects that I expected.
There is a controller:
exports.getUsers = function (req, res) {
let users = userModel.getAllUsers();
console.log(users); // here it outputs Promise { <pending> }
res.json(users);
};
How to get array instead of promise?
Let's break down your code. You are exporting a function of your design, getUsers, which is supposed to return a sequence of user objects. But it does not -- db.any(...) returns a Promise. How else do you think you can call then on the returned object? then is a member function of the Promise class, and you pass another function to then to work with the actual data (passed as parameter to the function you pass to then). Meaning that indeed data, as passed to the then call invoked on the return value of db.any(...) call, is the sequence of users you're after.
The mistake you are making is to assume that if you return data from the callback passed to then, it will become the return value of the then(...) call. It will not. then always returns a Promise -- whatever you return from the callback to then will become the resolved value of the returned promise, but it is the promise that is returned and that is why your users variable is a Promise.
You need to read more about promises, and how they are resolved asynchronously (between running your scripts), and how to synchronise your code on them resolving.
Hint: Use the await keyword to wait for and use the value given a promise, like the value you return from the callback that you pass to your then(...) call, or accept the design of your getUsers function returning a promise and adapt the rest of your code to work with that, without use of await.
Your getAllUsers function can be reduced to:
exports.getAllUsers = function () {
return db.any("SELECT * FROM users;");
}
...and with await you can then use it like:
let users = await getUsers();
The statement above has to be part of a function that is tagged as async though -- ECMAScript requires marking these explicitly in order to allow use of await expressions:
async function whatever() {
let users = await getUsers();
/// ...
}
Execution of a script that invokes an async function such as one containing the statement above, will be interrupted by the JavaScript interpreter at each await expression, and resumed when the promise expressed after the await keyword, resolves to a value, with the value being assigned to the users variable.
Using async and await does require a version of Node.js that can support them, but current release does (as of the time of writing this).
Otherwise you can still keep the getUsers function, but you need to use it differently since you can't use await -- having to use promises as one did prior to introduction of await and async, like:
getUsers().then(users => {
/// Do something with the `users` array.
});
At any rate, it looks like you have some holes in your understanding of how promises work, and I recommend you fill these holes by reading about them this time and not just going straight through to the pg-promise API, which builds on them.
Like you already found out userModel.getAllUsers() will return a promise and not an array. You need to wait for this promise to be resolved. This can be done using the an async function.
exports.getUsers = async function (req, res) {
let users = await userModel.getAllUsers();
console.log(users);
res.json(users);
};
Note that an async function will always return a promise.
Or you can make use of then method of the returned promise.
exports.getUsers = function (req, res) {
userModel.getAllUsers().then(users => {
console.log(users);
res.json(users);
});
};
I'm trying to use Promise.all() with an array of Promises that is being populated inside of a foreach looop right before but it seems like the Promise.all() is not waiting the promises to be all completed before executing its callback.
What's wrong with the following code? (I tried to simplify it before posting, so parts of it might not make complete sense, but that promised and loops are all there).
class test {
constructor(sql) {
Promise.all([this.sync(sql, 0), this.sync(sql, 1)]).then((data) => {
console.log(data);
});
}
sync(sql, id = 0) {
return new Promise((resolve, reject) => {
request.get('http://localhost/test/' + id, {
json: true
}, (req, res) => {
var promises = [];
res.body['items'].forEach(item => {
promises.push(new Promise((resolve, reject) => {
this.existingRecord(sql, item['id']).then(() => {
resolve(false);
}).catch(() => {
this.add(sql, item).then(resolve(id));
})
}))
});
Promise.all(promises).then((data) => resolve(data));
});
});
}
add(sql, data) {
return new Promise((resolve, reject) => {
console.log('Inserting ' + data['id']);
var request = new sql.Request();
var query = `INSERT INTO test (col1, col2) VALUES (${utils.prepareInsertdata(data)})`;
request.query(query, (err, result) => {
if (err) {
console.log('ERROR INSERTING: ' + data['id']);
console.log(err);
}
resolve();
});
});
}
}
First off, you're making it much harder to write good, clean, error-free code when you have a mix of promises and regular callbacks in your control flow. I find that the best way to write asynchronous code using promises is to first take any asynchronous operations that are not promise based and create promise-based wrappers for them and then write my logic and control flow using only promises. This makes a consistent path for flow of control and for error handling and it removes the mess of promisifying things from the actual main logic.
Then, I see several significant issues in your code.
Asynchronous operations in the constructor
It's almost never a good idea to put asynchronous operations in the constructor. This is because the constructor HAS to return the object itself so that leaves no simple way to communicate back to the code that created your object when the asynchronous operations are actually done and if they succeeded of failed. It is not entirely clear to me what you're trying to accomplish with those async operations, but this is likely a bad design pattern. I favor a factory function that returns a promise that resolves to the new object for combining the creation of an object with asynchronous operations. This gives you everything you need, a fully formed object, knowledge of when the async operations are done and an ability to have error handling for the async operations. You can see more about this factory function option and some other design options here:
Asynchronous operations in constructor
Improve .then() handler construction
When you do this:
this.add(sql, item).then(resolve(id));
You are calling resolve(id) immediately and passing that to .then() rather than waiting for the .then() handler to be called before calling resolve(id). All of this is complicated because you're mixing regular callbacks and promises.
Creating new wrapped promises rather than just returning existing promises
This is related to your mix of regular callbacks and regular promises, but you'd much rather just return an existing promise than wrap it in a new promise that you have to manually resolve and reject. More than half the time, you will miss proper error handling when manually wrapping things in a new promise and it just results in more code than is needed.
Race Conditions
In any sort of multi-user database environment, you can't write database code such as:
if (record exists) {
do one thing
} else {
create new record
}
This is a race condition. If some other database request comes in during the processing of this, it could change the database in the middle of this and you'd be trying to create a record that just got created by another piece of code.
The usual solution varies by database (and you don't say exactly which database library you're using). Usually, you want to let the database manage the creation of unique records making it so that a duplicate record (by whatever key you're managing uniqueness in this table by) isn't allowed in the database and the concurrency of that is managed by the database itself. Some databases have an atomic operation such as findOrCreate() that will find an existing record or create a new one in an atomic fashion. Other databases have other approaches. But, it's important to make sure that adding unique records to the database is an atomic operation that can't ever create unwanted duplicates.
I'd suggest this implementation:
// use promise version of request library (already promisified for us)
const rp = require('request-promise');
class test {
constructor() {
}
init(sql) {
return Promise.all([this.sync(sql, 0), this.sync(sql, 1)]).then((data) => {
console.log(data);
// do something with the data here - probably store it in instance data
});
}
sync(sql, id = 0) {
return rp.get('http://localhost/test/' + id, {json: true}).then(res => {
// process all items
return Promise.all(res.body.items.map(item => {
return this.existingRecord(sql, item.id).then(() => {
return false;
}).catch(() => {
// it's probably bad form here to do this for all possible database errors
// probably this should be looking for a specific error of id not found
// or something like that.
// This is also likely a race condition. You would typically avoid the race
// condition by making the item key unique in the database and just doing an add and letting
// the database tell you the add failed because the item already exists
// This will allow the database to control the concurrency and avoid race conditions
return this.add(sql, item);
});
}));
});
}
}
// factory function that returns promise that resolves to a new object
// don't use new test() elsewhere
function createTestObj(sql) {
let t = new test();
return t.init(sql).then(() => {
// resolve to our new object
return t;
});
}
For your add() method, I'd switch to using the promise interface in your sql database. There should either be one built-in or a 3rd party package that will add one on top of your database interface. This will prevent the manual creation of promises and the incomplete error handling in your add() method.
I am currently working an API/router which is to be integrated in the user's own web server. My code will do its required work but also allow the user to implement their own logic afterwards such as in this snippet: myApi.on('action', (err, result, req, res) => {/*user's logic here*/}).
By design, my code will not end the response if the user decides to override the default behavior and implement their own logic. This is to allow the request and response to fall through to the next middleware function if desired by the user.
The control flow I desire is:
Run my initial route logic
Run the user's custom logic
Pass control to the next middleware function if response hasn't ended
My issue arises when the user wants to implement any async logic. I have no way of allowing the user's custom logic to conclude before passing control onto the next middleware function. I believe it is because of the interface I have set up. I desire as little busywork as possible to be performed by the user. This means I would prefer not passing the user some equivalent of a callback() or next() function which they would need to call at every endpoint of their logic (as my router involves a number of different routes).
I've looked into promise wrappers such as promisify-node, but they seem only capable of wrapping functions which take a callback as their final argument. I've also considered requiring the user return a promise from their custom implementation, but that goes against my desire of not requiring busywork/boilerplate code.
Is there any way using promises -- or any other construct -- I can deal with this async issue without having to pain the user?
No, you cannot promisify a function that does not take a callback. Such a function either isn't asynchronous or doesn't provide you any way to know when it's finished - you're out of luck.
My issue arises when the user wants to implement any async logic.
Just require the user to pass a function that returns a promise if he wants to implement something asynchronous. It will probably do him a favour having to work with promises anyway :-)
const myApi = {
on(name, fn) {
Promise.resolve(result) // or whenever you need to run the user logic
.then(result =>
fn(req, result) // or whatever you want to pass
)
.then(customresult => {
res.write(customresult); // or whatever you want to do with its result
});
}
};
myApi.on('action', (req, result) => {
// user's logic here
// throw exceptions
// return values
// return promises for values
// or whatever the user needs to do
})
If I understand the question you would inspect the value the users' custom function returns. If it is a promise-like object then you can wait for it to resolve before moving on. This is equivalent to passing a next function to the users' custom middle ware.
In your example the user(s) would use your API like so:
myApi.on('action', (err, result, req, res) => {
return myPromiseAwareAsyncFunction();
})
And in your code:
let userMiddleWare = lookupNextActionHandler();
let result = userMiddleWare(err, result, req, res);
// Wrap any value (promise or not) in a promise
Promise.resolve(result)
.then(result => {
// Move on to next middle ware
})
.catch(error => {
console.error(error);
process.exit(1); // Or what ever you use
// to end the middle ware chain.
});
If you wish to offer both promises and a next callback you could do something like this:
let userMiddleWare = lookupNextActionHandler();
new Promise((resolve, reject) => {
function next(err) {
if (err) {
reject(err);
} else {
resolve();
}
}
let result = userMiddleWare(err, result, req, res, next);
Promise.resolve(result).then(resolve).catch(reject);
})
.then(result => {
// Move on to next middle ware
})
.catch(error => {
console.error(error);
process.exit(1); // Or what ever you use
// to end the middle ware chain.
});
Since promises can only handle one resolution and ignores further incantations after set this will work and which ever the user uses (next or return a Promise) will work.