Reputation: 1614
I'm assuming that I'm lacking some fundamentals on promises. I have a process that is within a AWS Lambda that downloads three files, then produces an output that is sent via email.
module.exports.test = async (event) => {
var p = download1();
var c = download2();
var h = download3();
await Promise.all([p, c, h]).then(() => {
... bunch of logic manipulating the data
customers.forEach(i => {
buildFile().then(data => {
sendEmail(data).then(response => {
console.log('Email sent successfully');
});
});
});
}, errHandler);
};
Both the buildFile and sendEmail functions return a Promise, but I never get to the 'Email sent successfully' message. It runs the code, but never actually returns before the Lambda completes (at least that's what I think is happening).
My understanding was that the Promise would fulfill the callback, but now I'm thinking I need to do something similar to how I did the downloads within the original Promise.all(). Is that the right direction?
The process should get the files, then it loops through customers to create each file and send via SES.
Upvotes: 1
Views: 426
Reputation: 750
@Bergi answer is correct, however I would like to expand it a little bit and give you some resources to increase or strength your Promises
knowledge. I'll use the next snippet of code, it's a bit cumbersome because I wrote it on Google Chrome
snippets so feel free to paste it there and play around with it:
(function() {
const promise1 = new Promise(function(resolve, reject) {
setTimeout(function() {
resolve('Replicant');
}, 300);
});
const promise2 = new Promise(function(resolve, reject) {
setTimeout(function() {
resolve('Human?');
}, 300);
});
function buildFile(type) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(`${type}`);
}, 300);
});
}
function sendMail(customer, answer) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(`Sent test to: ${customer}, he/she is a ${answer}`);
}, 300);
});
}
let customers = ['Rob Batty', 'Rachel', 'Deckard'];
async function myFunc() {
const [a, b, c] = await Promise.all([promise1, promise1, promise2]);
const answers = [a, b, c];
// const promises = customers.map(async (name, index) => {
// const file = await buildFile(answers[index]);
// const mail = await sendMail(name, file);
// console.log(mail);
// });
const promises = customers.map((name, index) => {
buildFile(answers[index])
.then(file => sendMail(name, file))
.then(sent => console.log(sent))
// If you're going to use Promises this is important! :D
.catch(err => console.log(err))
});
const [m1, m2, m3] = await Promise.all(promises);
console.log(m1, m2, m3);
}
myFunc();
})()
As pointed out in the answer, the problem is related to the use of forEach
, why? Well, simply because you're executing asynchronous
code on a synchronous
type of method, the don't get along very well :), so, the solution is to create an Array
of Promises
, like a Factory
. After the map
function the Promises
are Pending
nor Fullfiled
or Rejected
it's when you call the Promise.all()
method that you await for their results and they give you the values or in your use case, generate the file and then send the email to the user. I hope this helps you to get a better understanding on how Promises
works. I'll leave at the end two very important links, at least for me, that helped me out at some point with Promises
. Cheers, sigfried.
Upvotes: 1
Reputation: 664513
You're looking for
module.exports.test = async (event) => {
var p = download1();
var c = download2();
var h = download3();
try {
await Promise.all([p, c, h]);
// ... bunch of logic manipulating the data
var promises = customers.map(async (i) => {
var data = await buildFile();
var response = await sendEmail(data);
console.log('Email sent successfully');
});
await Promise.all(promises);
} catch(e) {
errHandler(e);
}
};
Your test
function didn't wait for the promises that you created in the forEach
loop, so the lambda completes before everything is done.
Upvotes: 6