Reputation: 81
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...
Upvotes: 1
Views: 6969
Reputation: 708026
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
})
}
});
});
}
Upvotes: 5