8

I'm using AWSs API Gateway along with a Lambda function for an API.

In my Lambda function, I have the following (simplified) code however I'm finding that the await sendEmail isn't being respected, instead, it keeps returning undefined

exports.handler = async (event) => {
    let resultOfE = await sendEmail("old@old.com", "new@new.com")
    console.log(resultOfE)
}

async function sendEmail(oldEmail, newEmail) {
    var nodemailer = require('nodemailer');

    var transporter = nodemailer.createTransport({
        service: 'gmail',
        auth: {
            user: 'xxx',
            pass: 'xxx'
        }
    });

    transporter.sendMail(mailOptions, function (error, info) {
        if (error) {
            console.log(error);
            return false
        } else {
            console.log('Email sent: ' + info.response);
            return true
        }
    });
}
userMod2
  • 8,312
  • 13
  • 63
  • 115
  • 1
    perhaps you need to `return start()` or `await start()` - since you `await doSomeOtherBusiness()` and currently `doSomeOtherBusiness` returns a Promise that resolves to `undefined` without regard to what is happening inside `start()` - to be honest, I can't see why your `doSomeOtherBusiness` would need that `start` function like that, seems like an overly complicated design – Jaromanda X Aug 05 '19 at 00:06
  • Thanks - so if within the `const start = async () => {` block there were some other awaits - would that cause any issues? – userMod2 Aug 05 '19 at 00:23
  • possibly - but it depends on how you write the code - if the code in `start` is doing everything correctly, then just do `async function doSomeOtherBusiness(someInput) { // Some business here }` - simples – Jaromanda X Aug 05 '19 at 00:24
  • Hmm - I think I'm fundamentally misunderstanding something - I'll update my code so the exact issue is clearer - as it appears its not awaiting anything – userMod2 Aug 05 '19 at 00:31
  • 1
    tagging a function `async` doesn't help with "callback" type functions - you'll need to "promisify" `transporter.sendMail` – Jaromanda X Aug 05 '19 at 00:34

2 Answers2

11

since you await sendMail, this requires sendMail to return a Promise - your code uses callbacks to handle the asynchrony, so

  • the async sendMail doesn't do anything (except make sendMail return a Promise that IMMEDIATELY resolves to undefined
  • you need to change sendMail to return a Promise (and it won't need async since it won't need await

the code below should do it -

var nodemailer = require('nodemailer'); // don't put require inside a function!!

exports.handler = async (event) => {
    const resultOfE = await sendEmail("old@old.com", "new@new.com")
    console.log(resultOfE)
}

//doesn't need async, since there will be no await
function sendEmail(oldEmail, newEmail) {
    return new Promise((resolve, reject) => { // note, reject is redundant since "error" is indicated by a false result, but included for completeness
        const transporter = nodemailer.createTransport({
            service: 'gmail',
            auth: {
                user: 'xxx',
                pass: 'xxx'
            }
        });
        transporter.sendMail(mailOptions, (error, info) => {
            if (error) {
                console.log(error);
                resolve(false);
            } else {
                console.log('Email sent: ' + info.response);
                resolve(true);
            }
        });
        // without the debugging console.logs, the above can be just
        // transporter.sendMail(mailOptions, error => resolve(!error));
    });
}

as per comment by @ThalesMinussi, transporter.sendMail returns a Promise if you do not provide a callback function, so you could write: (sendEmail is now async)

async function sendEmail(oldEmail, newEmail) {
    const transporter = nodemailer.createTransport({
        service: 'gmail',
        auth: {
            user: 'xxx',
            pass: 'xxx'
        }
    });
    try {
        const info = await transporter.sendMail(mailOptions);
        console.log('Email sent: ' + info.response);
        return true;
        }
    } catch (error) {
        console.log(error);
        return false;
    }
}
Jaromanda X
  • 53,868
  • 5
  • 73
  • 87
  • Thanks - thats much clearer - so the fundamental thing I was doing wrong was not actually returning any Promise. – userMod2 Aug 05 '19 at 01:06
  • well, you were (async functions always do) but the Promise you returned had nothing to do with the functions you were performing – Jaromanda X Aug 05 '19 at 01:13
  • Just for my betterment - `the Promise you returned had nothing to do with the functions you were performing` - do you mean my returning true or false? – userMod2 Aug 05 '19 at 01:24
  • no, your returning true or false actually did nothing - it does not return that value anywhere since it was in the callback to `transporter.sendMail` ... the promise your function was returning resolved to `undefined` (immediately) because `async` functions return the value returned in the function as a promise ... and since you have no `return` from the `sendEmail` function, this is equivalent to `return undefined` – Jaromanda X Aug 05 '19 at 01:29
  • Thanks - your explanation appreciated. – userMod2 Aug 05 '19 at 04:17
  • Nodemailer already returns a promise if you omit the callback, so essentially `await transporter.sendMail(args)` is enough. – Thales Minussi Aug 05 '19 at 08:41
  • I saw there was a great edit on your answer. Here's an upvote, sir. – Thales Minussi Aug 06 '19 at 05:11
0

You are invoking an async function inside of another function which is fine, however since it is returning a promise you have to await start as well.

Halil Irmak
  • 1,271
  • 1
  • 13
  • 24
  • to be honest, I'd simply write it as `function doSomeOtherBusiness(someInput) { const start = async () => { // Some business here } return start(); }` - note the lack of async on the outer function, since you're returning a Promise from the inner function - though, even this is odd, why have the inner function at all ... `async function doSomeOtherBusiness(someInput) { // Some business here }` – Jaromanda X Aug 05 '19 at 00:27