1

Ok, say I have two listeners with callbacks, and the code in one callback depends on a variable (UIDfromOnEndFunction) from the other callback.

For example:

//using andris9/mailparser on github

var mailparser = new MailParser({
    streamAttachments: true
}

// OnEnd Function
mailparser.on("end", function(objMail){
    **UIDfromOnEndFuntion** = objMail.UID;

    saveToDB("mail" + "1234", objMail);
});


mailparser.on("attachment", function(attachment){
    var output = fs.createWriteStream("attachments/"
        + **UIDfromOnEndFuntion** + "/" + attachment.generatedFileName);
        // need UIDfromOnEndFunction here

    attachment.stream.pipe(output);
});

How do I cause the callback in mailparser.on("attachment" to get the variable UIDfromOnEndFunction.

Does this involve promises? How do you do this?

blueuser
  • 151
  • 1
  • 1
  • 8

2 Answers2

0

You can do this via a closure: just access a variable from outside.

//using andris9/mailparser on github
var UIDfromOnEndFunction;

var mailparser = new MailParser({
    streamAttachments: true
}

// OnEnd Function
mailparser.on("end", function(objMail){
    UIDfromOnEndFuntion = objMail.UID;

    saveToDB("mail" + "1234", objMail);
});


mailparser.on("attachment", function(attachment){
    var output = fs.createWriteStream("attachments/" + UIDfromOnEndFuntion + "/" + attachment.generatedFileName);

    attachment.stream.pipe(output);
});

Please note my comment about ensuring end is called before attachment. If they do not fire in this way, this is fundamentally impossible.

Brendan
  • 2,777
  • 1
  • 18
  • 33
  • If `end` is called after `attachment`, you could I suppose [wait until the variable is changed](http://stackoverflow.com/questions/3635924/how-can-i-make-a-program-wait-for-a-variable-change-in-javascript), but I wouldn't advise that. – Brendan Dec 05 '14 at 06:31
  • I need a solution that handles the listeners `end` and `attachment` in any order of returning; it's why I asked the question. – blueuser Dec 05 '14 at 06:52
  • @blueuser that fundamentally goes against the ideas of asynchronous callbacks, because you want to handle them in a synchronous fashion: your only choice would be to wait until the variable is changed then. – Brendan Dec 05 '14 at 07:17
  • Could promises be used to achieve this? Do you know? (for instance promises seem to be used to "chain" asynchronous requests together sequentially with .then()). Maybe promises + closure? – blueuser Dec 05 '14 at 20:54
  • @blueuser I haven't done enough work with promises to definitively answer that, but maybe [this question](http://stackoverflow.com/a/25229057/2708970) would help you out. – Brendan Dec 06 '14 at 01:06
  • @"Brendan Ashworth" @Brendan Ashworth what do you think of the solution I posted, does that make sense? Do you have any reservations about it? – blueuser Dec 06 '14 at 10:04
0

OK I came up with a solution. It's based on closures from @Brenden Ashworth 's suggestion. It's untested but I'm fairly certain it would work, and I wanted to post this before I moved on as I found I didn't need to do what the original question described to get my project working.

However, I still think it is useful to have a solution to this type of problem as the need could arise, and I don't know a better solution.

Here's my solution:

//using andris9/mailparser on github

var mailparser = new MailParser({
    streamAttachments: true
}

var UIDfromOnEndFuntion;
var myAttachment;
var intNumberOfEmitsToEndAndAttachment = 0;


var funcBothEndAndAttachmentEmitted = function () {
    var output = fs.createWriteStream("attachments/"
        + UIDfromOnEndFuntion + "/" + myAttachment.generatedFileName);
            //UIDfromEndFunction should be garaunteed to be
            //populated by .once("end",...)

    myAttachment.stream.pipe(output);
        //myAttachment should be gauranteed to be populated
        //by .once("attachment",...)
}

mailparser.once("end", function(objMail){
    UIDfromOnEndFuntion = objMail.UID;

    saveToDB("mail" + "1234", objMail);

    intNumberOfEmitsToEndAndAttachment++;
    if (intNumberOfEmitsToEndAndAttachment == 2) {
        funcBothEndAndAttachmentEmitted();
    }
});


mailparser.once("attachment", function(attachment){
    myAttachment = attachment;

    intNumberOfEmitsToEndAndAttachment++;
    if (intNumberOfEmitsToEndAndAttachment == 2) {
        funcBothEndAndAttachmentEmitted();
    }
});

Now this would only work for a single emitted "end" and a single emitted "attachment".

You could get more creative with how the tracking is done to handle multiple attachments. For example, instead of using an integer to track the total number of calls, an array of objects could be used like [{"attachment",attachment_args1},{"attachment",attachment_args2},{"end",end_args2}] to do the tracking of calls (this would mean attachment has been called twice so far, and "end" once, for example, and you could trigger a function based on that knowledge like I do by calling funcBothEndAndAttachmentEmitted()).

I think this needs to be cleaned up and made into a library, unless there is a better way to do it that's not apparent. (Please comment if you know a better solution or I might go ahead and write a library for this solution.)

Another solution I thought of that might work is putting mailparser.once("attachment"...) inside of the callback for mailparser.once("end"...) but I suspect that wouldn't work if "attachment" is emitted first, and this solution seems a bit cludgey compared to a library-based solution if you're working with many different emitted events for some reason or different objects emitting different events.

blueuser
  • 151
  • 1
  • 1
  • 8