Unencoded
Unencoded

Reputation: 251

Express Routing and Asynchronous Code - How do I send the result of Asynchronous Code?

A simple outline of what I'm trying to achieve:

  1. An AJAX call is made to a specific URL on the server
  2. Express Routes (in "app.js") then run Node.js code within "Send.js"
  3. The output of "Send.js" is used as a response to the AJAX request with the "res.send" method.

My problem is that "Send.js" makes use of asynchronous methods and so the "response" variable I am trying to return in "res.send(response)" is not defined when the response is sent, as the "sendOn" function in "Send.js" completes before the asynchronous code does, naturally.

I know that using a callback is likely to be the solution here, so that "res.send(response)" is not called until response is defined, I don't know how to implement that given what I have across two separate files here:

app.js (simplified) :

var send = require("./Files/Other/Send.js");


    app.post('/Lamp-On', function (req, res)
    {
        var response = send.sendOn();
        res.send(response);
    });

Send.js (simplified) :

client.open and client.sendEvent are both asynchronous methods that accept a callback as a final argument, and are part of an external SDK.

var sendMessage = function ()
{
    var data = "on";
    var message = new Message(data);
    message.properties.add('Turn On');
    console.log('Sending message to turn the lamp: ' + message.getData());
    client.sendEvent(message, printResultFor('Message'));
};

var connectCallback = function () {
        if (err) {
            console.error('Could not connect: ' + err.message);
        }
        else {

            console.log('Client connected');

            client.on('message', function (msg) {
                console.log('Id: ' + msg.messageId + ' Body: ' + msg.data);
                client.complete(msg, printResultFor('completed'));
                // reject and abandon follow the same pattern.
                // /!\ reject and abandon are not available with MQTT
            });

            client.on('error', function (err) {
                console.error(err.message);
            });

            client.on('disconnect', function () {
                clearInterval(sendInterval);
                client.removeAllListeners();
                client.connect(connectCallback);
            });

            // Now call the sendMessage function.
              sendMessage();
        }
};

var sendOn = function () {
    client.open(connectCallback);
    return 
};

// Helper function to print results in the console
function printResultFor(op) {
  return function printResult(err, res) {
      if (err !== null) {
          console.log(op + ' error: ' + err.toString());
          console.log(err);
      }
        if (res) {
            console.log(op + ' status: ' + res.constructor.name);
        };
  };
}

exports.sendOn = sendOn;

I just don't how to deal with so many asynchronous callbacks!

Final Note: I am neither asking nor expecting anyone to just do this for me, but I do hope you can point me in the right direction, thanks in advance.

Upvotes: 0

Views: 725

Answers (2)

slebetman
slebetman

Reputation: 113916

The ONLY way to return a value is via callback. This is true even if you use a promise.

Callbacks is how you return a value. So since sendOn() needs to return a value it needs to accept a callback (alternatively return a promise and the promise will accept a callback).

Basically app.js would look like this:

app.post('/Lamp-On', function (req, res)
{
    send.sendOn(function(err,response){
        res.send(response);
    });
});

One of the things you need to get used to is to stop thinking of functions as subroutines. Instead think of functions as control structures like for or while or if. You're used to writing code like:

if (something) {
    something_else();
}

Conceptually you then shouldn't have much trouble with:

doIf(something,function(){
    something_else();
});

Now comes the interesting part. How to make sendOn() accept a callback? Just pass that callback all the way down to the function that "returns" the result. Basically, the process is inverted from procedural programming, instead of returning a value all the way to outer code you pass code all the way to the value:

var sendOn = function (callback) {
    client.open(function(){
        connectCallback(callback); // pass the CODE down to the value
    });
};

var connectCallback = function (callback) {
    if (err) {
        callback(err);
    }
    else {
        client.on('message', function (msg) {
            console.log('Id: ' + msg.messageId + ' Body: ' + msg.data);

            callback(null, msg.data); // Now pass the value to the CODE
        });

        sendMessage();
    }
};

I'm assuming that msg.data is the result. If not change it accordingly. Make sure you pass the result to the callback you've passed all the way down from sendOn().

Upvotes: 1

pytomaniaq
pytomaniaq

Reputation: 124

From my point of view and what I just learned some days ago it should work by simply adding whatever you want to do with the res object inside your sendMessage function (if possible). Because of beeing javascript the res-object should be accessible from there.

For further hints on callbacks and the callbackhell: http://callbackhell.com/

Upvotes: 1

Related Questions