Wait for AWS SNS publish callback to return a value to calling method

11,945

Solution 1

You can simply use callbacks for that. Modify your sendMessge like this

function sendMessage(message, phoneNumber, cb) {
    const params = { 
        Message: message, 
        MessageStructure: "string", 
        PhoneNumber: "+1" + phoneNumber
    };
    sns.publish(params, cb);
}

then on your main file you can supply callback like this

if (userProfile) {
  const message = "Your username is:\n" + userProfile.username;
  AWSSNSClient.sendMessage(message, phoneNumber, (err, data) => {
    if (err) {
      result.error = setTitleAndMessage("Error", "An error occurred");
    }
    else {
      result.success = setTitleAndMessage("Success", "Message sent");
    }
    res.send(result);
  });
}

Solution 2

Stumbled on this one via Google trying to figure this out myself today - short answer that I am now using:

You can now do this with Async/Await — and Call the AWS service (SNS for example) with a .promise() extension to tell aws-sdk to use the promise-ified version of that service function (SNS) instead of the call back based version.

The only caveat here is the containing function must ALSO be async to utilize the await syntax.

For example:

let snsResult = await sns.publish({
    Message: snsPayload,
    MessageStructure: 'json',
    TargetArn: endPointArn
}, async function (err, data) {
    if (err) {
        console.log("SNS Push Failed:");
        console.log(err.stack);
        return;
    }
    console.log('SNS push suceeded: ' + data);
    return data;
}).promise();

The important part is the .promise() on the end there. Full docs on using aws-sdk in an async / promise based manner can be found here: https://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/using-promises.html

In order to run another aws-sdk task you would similarly add await and the .promise() extension to that function (assuming that is available).

For anyone who runs into this thread and is actually looking to simply push multiple aws-sdk promises to an array and wait for that WHOLE array to finish (without regard to which promise executes first) I ended up with something like this:

let snsPromises = [] // declare array to hold promises
let snsResult = await sns.publish({
    Message: snsPayload,
    MessageStructure: 'json',
    TargetArn: endPointArn
}, async function (err, data) {
    if (err) {
        console.log("Search Push Failed:");
        console.log(err.stack);
        return;
    }
    console.log('Search push suceeded: ' + data);
    return data;
}).promise();

snsPromises.push(snsResult)
await Promise.all(snsPromises)

Hope that helps someone that randomly stumbles on this via google like I did!

Solution 3

stackdave will that actually wait?

Necevil "Search push suceeded will get logged twice" because you're mixing calling operations by passing a callback and using promises. You should only use one method of getting the result

let snsResult = await sns.publish({
    Message: snsPayload,
    MessageStructure: 'json',
    TargetArn: endPointArn}).promise()

will do the trick

Solution 4

Here the right updated API, August 2018, Necevil answer send the sms twice.

// using config.env
AWS.config.region = 'eu-west-1';
AWS.config.update({
  accessKeyId: process.env.AMAZON_SMS_ID,
  secretAccessKey: process.env.AMAZON_SMS_TOKEN,
});

// parameters 
let params = {
   Message: contentSMS,  // here your sms
   PhoneNumber: mobile,  // here the cellphone
 };


 const snsResult = await sns.publish(params, async (err, data) => {
    if (err) {
       console.log("ERROR", err.stack);
    }
    console.log('SNS ok: ' , JSON.stringify (data));
  });

Solution 5

If you're having issues with duplicate SNS messages being sent, I fixed this issue by utilizing examples from AWS:

// Load the AWS SDK for Node.js
var AWS = require('aws-sdk');
// Set region
AWS.config.update({region: 'REGION'});

// Create publish parameters
var params = {
  Message: 'MESSAGE_TEXT', /* required */
  TopicArn: 'TOPIC_ARN'
};

// Create promise and SNS service object
var publishTextPromise = new AWS.SNS({apiVersion: '2010-03-31'}).publish(params).promise();

// Handle promise's fulfilled/rejected states
publishTextPromise.then(
  function(data) {
    console.log("Message ${params.Message} send sent to the topic ${params.TopicArn}");
    console.log("MessageID is " + data.MessageId);
  }).catch(
    function(err) {
    console.error(err, err.stack);
  });

By utilizing a traditional .then() I was able to squash the duplicate message bug mentioned in comments above.

Share:
11,945
havak5
Author by

havak5

Updated on August 02, 2022

Comments

  • havak5
    havak5 almost 2 years

    I am attempting to send a text message when a user requests to reset their password. I would like to wait for the message to be sent to alert the user if it was successful or not. I am currently attempting to do it as follows:

    async function sendResetPasswordTextMessage(req, res) {
        let result = {};
    
        let phoneNumber = req.body.phoneNumber;                
    
        if (phoneNumber === undefined) {                       
            return sendInvalidParametersMessage(res);          
        }                                                      
    
        phoneNumber = phoneNumber.toString();                  
    
        const userProfile = await models.UserProfile.findOne({ 
            where: {                                           
                phoneNumber: phoneNumber                       
            }                                                  
        });                                                    
        ************************** RELEVANT CODE TO ISSUE *************************
        if (userProfile) {
            const message = "Your username is:\n" + userProfile.username;
            const sent = await AWSSNSClient.sendMessage(message, phoneNumber);
    
            if (!sent) {
                result.error = setTitleAndMessage("Error", "An error occurred");
            } else {
                result.success = setTitleAndMessage("Success", "Message sent"); 
            }
        }
        return res.send(result);
        ***************************************************************************
    }
    

    In my other class AWSSNSClient, I have the following sendMessage function:

    function sendMessage(message, phoneNumber) {
        const params = { 
            Message: message, 
            MessageStructure: "string", 
            PhoneNumber: "+1" + phoneNumber
        };
        let sent = false;
        sns.publish(params, function(err, data) {
            if (err) {
                console.log(err, err.stack); // an error occurred
            }
            else {
                sent = true;
            }
        });
    
        return sent;
    }
    

    I cannot figure out how to make sendMessage wait for sns.publish to return before it returns itself. I have tried making it an async method and adding await on sns.publish, but the function still returns before sent gets set to true.

    I know that the messages are sending without error because I am receiving them and no console logs are printed.