Javascript - can't resolve this 'warning: promise was created in a handler' - javascript

Using sequelize.js in a nodejs app, and I have a promise.all that takes two promises (a user query, and a color query):
router.get(`/someEndPoint`, (req, res) => {
let userAccount = user.findOne({
where: {
id: //some ID
}
});
let colorStuff = color.findOne({
where: {
colorName: //some color
}
})
Promise.all([userAccount , colorStuff ]).then(([result1, result2]) => {
//do stuff, such as:
res.send('success');
}).catch(err => {
console.log(err)
});
});
At the part that says //do stuff, my console keeps giving me this warning:
a promise was created in a handler at... but was not returned from it,
see (URL that I can't post) at Function.Promise.attempt.Promise.try
I'm not sure how to resolve this. I thought after the .then that the promises are resolved?

Hard to tell without other context, but perhaps you need to return the Promise.all
return Promise.all([user, color])...
From the bluebird docs here: https://github.com/petkaantonov/bluebird/blob/master/docs/docs/warning-explanations.md#warning-a-promise-was-created-in-a-handler-but-was-not-returned-from-it
if there are any other promises created in the // do stuff area, be sure to return those as well.

Related

How can I use the request object in a nested promise?

I am working on a Nuxt server side rendered application with the express framework. For authentication I am using the openid-client package. Now I want to store my retrieved token in the express session but the request model (req) is always undefined in the callback promise. To do this I want to use req.session.token = tokenSet.access_token. I am a Newbie in JavaScript so I think I am missing something obvious.
I tried various options on how to pass variables into a JavaScript promise but all of these required that you define the Promise by yourself which is not my case. I also tried waiting on the promise and using it outside of the callback promise but had no success either.
router.get('/api/oidc-callback', (req, res, params) => {
Issuer.discover('http://localhost:5000') // => Promise
.then(function(identityIssuer) {
const client = new identityIssuer.Client({
...
})
// HERE IT IS DEFINED
console.log(req)
client
.callback('http://localhost:3000/api/oidc-callback', req.query, {
code_verifier
})
// => Promise
.then(function(tokenSet) {
// HERE IT IS UNDEFINED
console.log(req)
req.session.token = tokenSet.access_token
}, req)
.catch(error => {
console.log(error)
})
//Also tried using outside
res.redirect('/oidc-callback')
})
})
Thanks in advance for your help!
You have two nested asynchronous operations (shown simplified here) and then you try to do something last in the first .then() handler:
Issuer.discover().then(function() {
client.callback().then(function() {
// ...
});
res.redirect('/oidc-callback');
});
This causes res.redirect() to get called before client.callback() is done as there is nothing in your code that make it wait for the completion of client.callback(). That sends the response and triggers a redirect before you modify the session which is not what you want to do. You can fix that in one of two ways:
1) Put the res.redirect() inside the inner .then() like this:
Issuer.discover().then(function() {
client.callback().then(function() {
// ...
res.redirect('/oidc-callback');
});
});
2) Add a return before client.callback() so that it will chain the inner promise to the outer one. Then, the outer one won't finish until the inner one is done and you can add another .then() handler to put the res.redirect() into:
Issuer.discover().then(function() {
return client.callback().then(function() {
// ...
});
}).then(function() {
// gets called when both asynchronous operations are done
res.redirect('/oidc-callback');
});
I'd recommend option #2 because it makes error handling simpler as you can do all your error handling in one place at the top level. Putting all that together, you'd end up with this:
router.get('/api/oidc-callback', (req, res, params) => {
Issuer.discover('http://localhost:5000').then(function(identityIssuer) {
const client = new identityIssuer.Client({
...
})
return client.callback('http://localhost:3000/api/oidc-callback', req.query, {
code_verifier
}).then(function(tokenSet) {
console.log(req);
req.session.token = tokenSet.access_token
}, req);
}).then(() => {
res.redirect('/oidc-callback');
}).catch(err => {
console.log(err);
res.sendStatus(500);
});
});
Note, I also added proper error handling at the end. This makes sure that no response is sent until both your async operations are done and if either one fails, it sends a proper error response.
I believe the reason why req is undefined because the promise was resolved after the execution of the middleware (res, req, next) =>{...}. Try returning the top level promise i.e. (res, req, next) =>{ return Issuer.discover(...)}, also add a return statement before client.callback(...).
router.get('/api/oidc-callback', (req, res, params) => {
return Issuer.discover('http://localhost:5000') // <-- added return here
.then(function(identityIssuer) {
const client = new identityIssuer.Client({
...
})
// HERE IT IS DEFINED
console.log(req)
return client // <-- added return here
.callback('http://localhost:3000/api/oidc-callback', req.query, {
code_verifier
})
// => Promise
.then(function(tokenSet) {
// HERE IT IS UNDEFINED
console.log(req)
req.session.token = tokenSet.access_token
res.redirect('/oidc-callback')
}) // removed , req here, it is not needed
.catch(error => {
console.log(error)
})
})
})
By adding the return statement, it tells express you are running an async function, thus express it going to wait until your middleware is resolved before moving on to the next middleware.

Is using a promise inside another promise considered an anti-pattern?

I have a code that looks like this :
app.post("/api/exercise/add", function(req, res, next) {
User
.findById(req.body.userId)
.exec()
.then(user => user)
.then(function(user) {
let exercise = new Exercise({
description: req.body.description,
duration: req.body.duration,
date: req.body.date, //BUG: must add validations, date accepts 19984-01-01
user: user
})
.save()
.then(function(exercise) {
user.exercises.push(exercise)
user.
save().
then(user => res.json({ status: 201, exercises: user.exercises }))
})
.catch(err => next(err))
})
.catch(err => next(err));
});
Is the fact that I'm using a promise inside another promise, in this case, considered an anti-pattern?
In a sense, it's inelegant - the problem is that it creates unnecessary .then nesting. If the .then and .catch handlers that follow both Promises are the same, you can just return the new Promise inside the .then to pass it onto the next .then or .catch, as in the code below.
To pass along multiple variables / Promises to the next .then without reassigning an outer variable, use Promise.all:
app.post("/api/exercise/add", function(req, res, next) {
User
.findById(req.body.userId)
.exec()
.then(function(user) {
// return the Promise so it can be used by the next then, without nesting
// because you also need access to `user` in the next then, use Promise.all
return Promise.all([user, new Exercise({
description: req.body.description,
duration: req.body.duration,
date: req.body.date, //BUG: must add validations, date accepts 19984-01-01
user: user
})
.save()]);
})
.then(function([user, exercise]) {
user.exercises.push(exercise);
// return the Promise so it can be used by the next then, without nesting:
return user.save();
})
.then(user => res.json({ status: 201, exercises: user.exercises }))
.catch(err => next(err));
});
Note that the
.then(user => user)`
is completely superfluous - it doesn't do anything, you already have a Promise that resolves to the user you want in the next .then.
We can have something like this:
new Promise((resolve, reject) => {
let x = 25;
if (x%2 === 0) {
return Promise.resolve('even');
} else {
return Promise.resolve('odd');
}
})
.then(result => {
console.log('the number is '+result);
});
In this case, both the branches of the condition are homogeneous, they both return a string and the result is handled in the same way.
But this not always happens, for example:
new Promise((resolve, reject) => {
if (user.type === 'admin') {
return this.userService.getAdminTools();
} else {
return this.userService.getUserTools();
}
})
.then(result => {
// What type is the result? Maybe in this case, chaining is not the best solution!
});
If you have more branches and the result is not homogeneous, maybe chaining is not the best choice. You can listen to the Promise inside another Promise, or you can just call another method which contains the async code
Your execution flow is now separated into multiple branch which can be a wanted behavior.
When writing code, you should always think about re-usability and readability.
How an other programmer would read and understand my code easily and without getting an headache ?
The way you are putting it together is hard to follow. You should put the asynchronous action you want to perform into a separated function.
Breaking complex stuff into functions are a good practice to use in general, not only in this particular case. Try to have one function to do one thing, and have one execution flow.
User
.findById(req.body.userId)
.exec()
.then(user => user)
.then(user => asynchronousAddUser(user))
.catch(err => next(err));
It is not necessarily an antipattern, but it depends a lot on why are you doing it.
There may be valid reason for breaking the chain and starting a new one, but if you find yourself doing that too often then something is wrong and probably you should rethink your flow.
I see 2 common reasons why people tend to start a new chain
1.A handler at some point during the chain makes a decision based on a condition and each branch has a totally different way of doing its work. At this point is perfectly valid to start a new chain, but I would create a new method that returns a promise. The next handler in the chain must be aware about the fact that it may receive heterogenous data
NewPromise()
.then( res => {
if (someCond) {
return OtherPromise(args)
}
....
return obj
})
.then( res => {
//this promise must be aware that res may be heterogeneous
})
2.During the chain a handler receives some info that you can't easily propagate down the chain. For instance when needing 2 different piece of information that come from a DB and you need both in the end to do the work.
User.findById(uid1)
.then(user1 => {
return User.finById(uid2)
})
.then(user2 => {
// at this point user1 is not available any more
})
A solution for this is to have a variable outside of the chain and not start a new chain
var user1
User.findById(uid1)
.then(user => {
user1 = user
return User.finById(uid2)
})
.then(user2 => {
// at this point user is available and has the value of user1
})

What are the down sides to wrapping promises in an object that resolves them?

I'm working on a new framework of microservices built in Node 8 and trying to simplify some of the logic required for passing Promises around between services.
I have a function I import in each service called StandardPromise which you can pass a Promise to. StandardPromise will call .then() on the promise and place the result in an object. If the promise was resolved it will be placed in the data attribute, if was rejected or threw an error then that will go in the err attribute.
The result of the above is that when a service receives a standardized promise by awaiting a call to another service, it can just check if there's anything in err and move forward with data if err is empty. This flow is significantly simpler than having .then() and .catch() blocks in every function.
I'm pretty happy with how it's turning out, and it seems to be working great, but since I haven't seen many examples of this kind of flow I want to know if there's something I'm missing that makes this a terrible idea or an antipattern or anything like that.
Here's a simplified, somewhat pseudocode example:
Service1:
const sp = require('./standardPromise');
const rp = require('request-promise-native');
function ex() {
// Wrap the Promise returned from rp as a "standardPromise"
return sp(rp.get({url: 'https://example.com'}));
}
Service2:
const Service1 = require('./Service1');
async function ex2() {
var res = await Service1.ex();
if (res.err) {
// Do error stuff
console.error(res.err);
return;
}
// Here we know res.data is our clean data
// Do whatever with res.data
return res.data;
}
standardPromise:
module.exports = function(promise) {
try {
return promise.then((data) => {
return {err: undefined, data: data};
}).catch((err) => {
return Promise.resolve({err: err, data: undefined});
});
} catch(err) {
console.error('promise_resolution_error', err);
return Promise.resolve({err: err, data: undefined});
}
}
It can just check if there's anything in err and move forward with data if err is empty. This flow is significantly simpler than having .then() and .catch() blocks in every function.
No, this is much more complicated, as you always have to check for your err. The point of promises is to not have .catch() blocks in every function, as most functions do not deal with errors. This is a significant advantage over the old nodeback pattern.
You would drop your standardPromise stuff and just write
// Service1:
const rp = require('request-promise-native');
function ex() {
return rp.get({url: 'https://example.com'});
}
// Service2:
const Service1 = require('./Service1');
async function ex2() {
try {
var data = await Service1.ex();
} catch(err) {
// Do error stuff
console.error(err);
return;
}
// Here we know data is our clean data
// Do whatever with data
return data;
}
or actually simpler with then for handling errors:
// Service2:
const Service1 = require('./Service1');
function ex2() {
return Service1.ex().then(data => {
// Here we know data is our clean data
// Do whatever with data
return data;
}, err => {
// Do error stuff
console.error(err);
});
}

How to return value from a promise that's inside another function?

I know that a lot of questions have been asked about async programming and Promises, but I really need an example with this specific code to figure out how I should go about returning promises that's being returned in another function.
I have two functions. The first one is called upon GET'ing to a route. This route should create a payment link and save a booking to a database.
exports.create_booking = function(req, res) {
req.body.payment = exports.create_booking_payment(req.body.total_amount);
console.log(req.body.payment); // This returns ' Promise { <pending> } '
var new_booking = new Booking(req.body);
new_booking.save(function(err, booking) {
if (err)
res.send(err);
res.json(booking);
});
};
However creating the payment link happens with an asynchronous method. My first problem was that I could only access the payment inside the methods callback function.
Now I have wrapped the method inside another (async) method in which a Promise is created and resolved. This method is being returned to my first method with an await statement, but all this returns is: ' Promise { } '.
I know that this happens because the method is being returned before the promise is resolved. But I don't understand why this is. My assumption is that the 'await' statement makes sure to wait returning the method before the async function is completed.
exports.create_booking_payment = async function() {
function asyncPayment() {
return new Promise (function(resolve, reject) {
mollie.payments.create({
amount: 20.00,
description: "Reservation code: ",
redirectUrl: "https://www.website.com/",
webhookUrl: ""
}, function(payment) {
if (payment.error) reject(payment.error)
else { resolve({
id: payment.id,
link: payment.getPaymentUrl(),
status: payment.status
})
}
});
});
}
return await asyncPayment();
}
I hope someone can help me out here...
You seem to have missed that an async function still returns a promise, not the actual value. So, when you call create_booking_payment(), you are getting back a promise that you need to use either .then() with or await with. There's no free lunch across a function boundary. await allows you to program in a synchronous-like fashion inside a function, but still does not allow you to return the value from the function. When it looks like you're returning the value from the async function, you're actually returning a promise that resolves to that value.
So, you'd do this with async and await:
exports.create_booking = async function(req, res) {
try{
req.body.payment = await exports.create_booking_payment(req.body.total_amount);
console.log(req.body.payment);
var new_booking = new Booking(req.body);
new_booking.save(function(err, booking) {
if (err)
res.status(500).send(err);
else
res.json(booking);
});
} catch(e) {
res.status(500).send(err);
}
};
or this with .then():
exports.create_booking = function(req, res) {
exports.create_booking_payment(req.body.total_amount).then(payment => {
console.log(payment);
req.body.payment = payment;
var new_booking = new Booking(req.body);
new_booking.save(function(err, booking) {
if (err)
res.status(500).send(err);
else
res.json(booking);
});
}).catch(err => {
res.status(500).send(err);
});
};
Note, I also added more complete error handling to both scenarios. Also, this code would be a lot cleaner if you "promisfied" or used an already promisified interface for your .save() method. I strongly dislike using plain callback async code inside of promise-based code because it ends up duplicating error handling (like you see in this case).
Also, create_booking_payment() doesn't need to be async or use await since all you need it to do is to return your promise which it already knows how to do:
exports.create_booking_payment = function() {
return new Promise (function(resolve, reject) {
mollie.payments.create({
amount: 20.00,
description: "Reservation code: ",
redirectUrl: "https://www.website.com/",
webhookUrl: ""
}, function(payment) {
if (payment.error) reject(payment.error)
else { resolve({
id: payment.id,
link: payment.getPaymentUrl(),
status: payment.status
})
}
});
});
}

What the right way to use Promises with mongoose?

I'm new with Promises and in two days a already saw at least five ways to do.
Some ways are older versions, others focus on other languages, so I decide to ask what should be used today.
I did it this way, with 'err' and 'doc' parameters:
Proposals.findById({ ...body, user }, (err, doc) =>
Advertisements.findOneAndUpdate(
{_id: body.advertisement}, {$push: {proposals: doc._id}}
)
)
.then((Proposals) => Proposals.view())
.then(success(res, 201))
.catch(next)
And this:
Proposals.create({ ...body, user })
.then((Proposals) => {
Advertisements.findOneAndUpdate(
{_id: body.advertisement}, {$push: {proposals: Proposals._id}}
)
.catch(Advertisements)
return Proposals.view()
})
.then(success(res, 201))
.catch(next)
Without the "catch" and "return" itsn't work, but I don't know why exactly. :x
The answer to
Using Promises in Mongoose Routes
seems like a beautiful but I couldn't reproduce in code above.
return findUser(userId)
.then((user) => findReceipt(user.bookName))
.then((receipt) => res.status(200).json(receipt))
.catch((err) => res.status(500).json(null))
Are any of these codes more right?
Could you help me to fix the second code?
Thank you.
Let me detail the syntax of the first block of code:
Proposals.findById({ ...body, user }, (err, doc) => {
return Advertisements.findOneAndUpdate(
{_id: body.advertisement}, {$push: {proposals: doc._id}}
);
})
.then((Proposals) => { return Proposals.view() })
.then(success(res, 201))
.catch(next)
In ES6 syntax, this function:
() => ('test');
Equals to
() => { return 'test' };
So I believe your main problem is because of not understanding exactly the ES6 syntax at the beginning.
You should follow these best practices when it comes to Promises:
Avoid nesting catch in Promises
Chain Promises by returning them and use them in the then
Hope it helps.
There are number of ways to handle promises in Mongoose. The exact way that you decide to implement it is left to personal preference, but you need to make sure that your order of execution is as expected. In your first example you are mixing a callback with promises, which I personally think is not optimal. The third example is cleaner as it is a chain of promises being passed down via the then calls.
The important thing to note is that if you want to run async code inside your then calls, you need to ensure you return a Promise that resolves at the right moment. In your second example, you are possibly returning before your findOneAndUpdate call is finished executing. You can avoid this in a number of ways. One option would be to wrap the second call inside a Promise. I give you an idea below by mixing parts of your second and third examples and assuming that the return value from proposal.view() is not async. I use explicit return statements for clarity.
Proposals.create({ ...body, user })
.then((proposal) => {
// call returns a new promise
return new Promise((resolve, reject) => {
// make second async call to database
Advertisements.findOneAndUpdate(
{ _id: body.advertisement },
{ $push: { proposals: proposal._id } },
)
.then(() => {
// promise resolves here and passes return
// value of proposal.view() to next `then`
return resolve(proposal.view())
});
});
})
// consume the value from proposal.view() and send it in response
.then(proposal => res.status(200).json(proposal))
.catch(next)
This would mean that the return value of proposal.view() will only be passed onto the next then once the findOneAndUpdate call is complete.
The example code is not waiting for, or using the result of the update.
In promises returning a promise and using .then waits for that async operation. You can then check the result if required:
Proposals.create({ ...body, user })
.then((proposal) => {
return Advertisements.findOneAndUpdate(
{_id: body.advertisement}, {$push: {proposals: proposal._id}}
)
})
.then((update) => {
if (!update) throw new Error(`Could not find advertisement for "${user}"`)
success(res, 201))
})
.catch(next)
Or the equivalent ES2017 code using await to wait for the promise to resolve, which should help reason how the above Promise .then code flow works.
async function handler(){
try {
let proposal = await Proposals.create({ ...body, user })
let update = await Advertisements.findOneAndUpdate(
{_id: body.advertisement}, {$push: {proposals: proposal._id}}
)
if (!update) throw new Error(`Could not find advertisement for "${user}"`)
success(res, 201)
}
catch (err) {
next(err)
}
}

Categories

Resources