"if" within a loop not sending messages in correct order

I have a loop that works, and within that i'm using "if" to check the difference in days between 2 dates. When the "if" is matched I'm setting some flow variables which are used by the next node to send emails with other flow variable data.

For testing the incoming array has 2 entries and their expiry dates match the 30 day difference. They are correctly matched by the "if" statement.

As below debug output, within the loop the email addresses at array index 0 and 1 are correctly pulled out and the loop counter increments from 0 to 1. However, it appears from the order of the debug output the node.send's don't execute until the loop has finished, and this results in the next node only using the 2nd email address thats stored in the flow variable at that time (same for the other flow variables).

The debug message order I would expect to see is:
emailaddress-1 / loop count: 0 / debug 1 / emailaddress-1 then emailaddress-2 / loop count: 1 / debug 1 / emailaddress-2

I've tried using node.send in different places with no succcess. I also read up a bit on " Asynchronous context access" which might be related to my issue, but couldn't figure it. Bit confused about whether it should be synchronous or asynchronous processing and how to achieve the correct result.

Sure its a school boy error :roll_eyes:

24/09/2021, 20:21:08node: Check for expiring DPSK & send EMail notification
function : (warn)
"email: emailaddress-1"

24/09/2021, 20:21:08node: Check for expiring DPSK & send EMail notification
function : (warn)
"loop count: 0"

24/09/2021, 20:21:08node: Check for expiring DPSK & send EMail notification
function : (warn)
"email: emailaddress-2"

24/09/2021, 20:21:08node: Check for expiring DPSK & send EMail notification
function : (warn)
"loop count: 1"

24/09/2021, 20:21:08node: debug 1
msg : Object
{ _msgid: "6483b845c5ad0ace" }

24/09/2021, 20:21:08node: debug 1
msg : Object
{ _msgid: "6483b845c5ad0ace" }

24/09/2021, 20:21:08node: Check DPSK Action & Format Notifications
function : (warn)
"email: emailaddress-2"

24/09/2021, 20:21:08node: Check DPSK Action & Format Notifications
function : (warn)
"email: emailaddress-2"

image

First node:

//create newMsg & initialise variables
var newMsg = {};
var datefromDPSKexpiry = "";
var microSecondsDiff = "";
var daysDiff = "";
var todaysDateTimeStamp = new Date(flow.get("todaysDate")).getTime();

//Set totalCount to number of Active DPSK received in the array
var totalCount = msg.payload.page.totalCount;

//Loop through the received DPSK array and check expiration = 14 days
//If it is then format and send EMail notification
for(counter = 0; counter < (totalCount); counter++) {
    flow.set("dpskExpiring", false);

    datefromDPSKexpiry = new Date(new Date(msg.payload.contents[counter].expirationDateTime)).getTime();
    microSecondsDiff = Math.abs(datefromDPSKexpiry - todaysDateTimeStamp);
    // Math.round is used instead of Math.floor to account for certain DST cases
    // Number of milliseconds per day = 24 hrs/day * 60 minutes/hour * 60 seconds/minute * 1000 ms/second
    daysDiff = Math.round(microSecondsDiff / (1000 * 60 * 60  * 24));

    //Check if the current DPSK being checked in the array DPSK will expire in 30 days
    //If it does then send EMail notification
    if (daysDiff === 30) {
        flow.set("dpskExpiring", true)
        flow.set("serviceLevel", msg.payload.contents[counter].name);
        flow.set("userEmail", msg.payload.contents[counter].thirdPartyId);
        flow.set("dpskPassphrase", msg.payload.contents[counter].passphrase);
        flow.set("dpskExpiry", msg.payload.contents[counter].expirationDateTime);
        
        node.warn("email: " +flow.get("userEmail"));
        node.warn("loop count: " +counter);
        
        node.send(newMsg);
        node.done();
    }
}
return;

Second Node:

//If DPSK is Expiring soon based on # of days left threshold then format EMail Notification & send
if (flow.get("dpskExpiring") === true) {
    
    node.warn("email: " +flow.get("userEmail"))
    
    newMsg.topic = "Your Wi-Fi Access Key (passphrase) is due to Expire";
    newMsg.payload = "<font size = 3>Your Wi-Fi Access Key <B>"+flow.get("dpskPassphrase")+"</B> for the Wi-Fi network <B>"+flow.get("wifiNetwork")+"</B> will expire in 2 weeks time.<BR><BR>The Access Key is valid until <B>"+flow.get("readableExpiryDate")+"</B><BR><BR>Your current Service is: <B>"+flow.get("serviceLevel")+"</B><BR><BR>Please renew your access key by clicking the link below<br><a href='http://10.10.10.10:1880/Login'>Login to Tenant Wi-Fi Access Portal</a>";
    return [null,newMsg]; 
}

The node.send() calls execute immediately, but the messages will be queued at the front of the next node. The function will run till it finishes before the next node gets run. The solution is not to use context, but to put the data into the message and pass it on that way. So put the data into newMsg before sending it.
Move the declaration of newMsg inside the loop though, and change it to
let newMsg = {}
then it will be a new message each time round the loop, otherwise you will be repeatedly changing the same newMsg and sending it. Because javascript objects are accessed by reference then the message effectively contains a pointer to newMsg and if you don't make a new one each time then all the messages will reference the same object and you will have the same problem.

Don't set context, just send an object with all the properties you need.

Thx Steve and Colin for the suggestions.
I have changed the first node to this (just an extract of the part changed, excludes the loop):

    if (daysDiff === 29) {
        let newMsg = {};        
        newMsg.payload = {
        "dpskExpiring": true,
        "serviceLevel": msg.payload.contents[counter].name,
        "userEmail": msg.payload.contents[counter].thirdPartyId,
        "dpskPassphrase": msg.payload.contents[counter].passphrase,
        "dpskExpiry": msg.payload.contents[counter].expirationDateTime
        }
        node.send(newMsg);
        node.done();        
    }

I have updated the next node as below, using flow.set for email address as the next node on uses that to set the email To: field.

if (msg.payload.dpskExpiring === true) {
    flow.set("userEmail", msg.payload.userEmail);
    node.warn("email is: "+flow.get("userEmail"));
    newMsg.topic = "Your Wi-Fi Access Key (passphrase) is due to Expire";
    newMsg.payload = "<font size = 3>Your Wi-Fi Access Key <B>"+msg.payload.dpskPassphrase+"</B> for the Wi-Fi network <B>"+flow.get("wifiNetwork")+"</B> will expire in 2 weeks time.<BR><BR>The Access Key is valid until <B>"+flow.get("readableExpiryDate")+"</B><BR><BR>Your current Service is: <B>"+msg.payload.serviceLevel+"</B><BR><BR>Please renew your access key by clicking the link below<br><a href='http://194.60.94.30:1880/Login'>Login to Tenant Wi-Fi Access Portal</a>";
    return [null,newMsg]; 
    }
return;

This is the formatting of the email before its passed to the send email node. I'm using flow.get for userEmail as other parts of my workflow also feed into this node for email notifications and the email address gets set correctly.

newMsg = {};
newMsg.to = flow.get("userEmail");
newMsg.from = Settings.EMAIL.EMAILfrom;
newMsg.topic = msg.topic;
newMsg.payload = msg.payload;
return newMsg;

When I now test the result is as below in debug. An improvement, but the actual emails still both get sent to the 2nd email address in the original array. The other parameters passphrase and service are correct (unique) for each user in the email.

25/09/2021, 08:59:42node: Check DPSK Action & Format Notifications
function : (warn)
"email is: emailaddress-1"

25/09/2021, 08:59:42node: debug 1
msg : Object
object
payload: object
dpskExpiring: true
serviceLevel: "AFFORDABLE-SILVER"
userEmail: "emailaddress-1"
dpskPassphrase: "9rf2UHcP1LqP"
dpskExpiry: "2021-10-24T10:33:49Z"
_msgid: "62ca95e3484a2ebd"
settings: object

25/09/2021, 08:59:42node: Check DPSK Action & Format Notifications
function : (warn)
"email is: emailaddress-2"

25/09/2021, 08:59:42node: debug 1
msg : Object
object
payload: object
dpskExpiring: true
serviceLevel: "PRIVATE-SILVER"
userEmail: "emailaddress-2"
dpskPassphrase: "daLb6MEKUmD7"
dpskExpiry: "2021-10-24T10:37:43Z"
_msgid: "62ca95e3484a2ebd"
settings: object

This a debug output connected to the node that submits the data to be sent by email send node. You can see its the same To: email adress in both, the first should be emailaddress-1. The passphrase and service parameters are however correctly unique.

25/09/2021, 09:10:38node: debug 2
Your Wi-Fi Access Key (passphrase) is due to Expire : msg : Object
object
to: "emailaddress-2"
from: "alert@comm.com"
topic: "Your Wi-Fi Access Key (passphrase) is due to Expire"
payload: "Your Wi-Fi Access Key 9rf2UHcP1LqP for the Wi-Fi network Tenant Wi-Fi will expire in 2 weeks time.

The Access Key is valid until Sunday, October 24th 2021 at 00:01

Your current Service is: AFFORDABLE-SILVER

Please renew your access key by clicking the link below
Login to Tenant Wi-Fi Access Portal"
_msgid: "4562596db27ac25d"

25/09/2021, 09:10:38node: debug 2
Your Wi-Fi Access Key (passphrase) is due to Expire : msg : Object
object
to: "emailaddress-2"
from: "alert@comm.com"
topic: "Your Wi-Fi Access Key (passphrase) is due to Expire"
payload: "Your Wi-Fi Access Key daLb6MEKUmD7 for the Wi-Fi network Tenant Wi-Fi will expire in 2 weeks time.

The Access Key is valid until Sunday, October 24th 2021 at 00:01

Your current Service is: PRIVATE-SILVER

Please renew your access key by clicking the link below
Login to Tenant Wi-Fi Access Portal"
_msgid: "4562596db27ac25d"

Isn't it exactly the same problem? The setting in flow context "userEmail" is getting overwritten before it is sent to the email node. Don't use context, pass it to the email node in the message.

Yeah, just further down the flow :frowning:

The reason I am setting it is as a flow variable is because there are other workflow branches that also feed into the same email formatting node before sending, and they all works fine. Can't understand why it gets over-written in this case when it looks correct up to that point.

I'll update it so not using flow variable for this part of the workflow.

Thx again for the pateint feedback.

As before, because you are sending two messages in quick succession (unless I misunderstand what you are doing) so the second one overwrites the flow variable before the email formatting node gets to handle the first one.

Change them all to pass it in the message. It really is safer to not use context in this situation (in fact I never use it for such things). Pass it in the message. I don't know the rest of your flows, but if you have multiple places where you setup the userEmail context var then are you absolutely certain that there is no possibility that two of those places could get messages very close together? It may work for a year without a problem and then by coincidence that happens and then you will send a email to the wrong address.

Ok, changed that part of the workflow to pass the email as newMsg.userEmail and all works now. Thx for the pointers.

By the way, you generally don't need to (and in fact shouldn't) make a new message in a function unless you are sending multiple messages using node.send(). It is often useful to be able to pass message properties through a node unaffected, and those will get lost if you make a new message. Instead, modify the msg passed in and send that on.

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.