1

I'm pretty new to Node.js and Javascript, so please forgive my confusion about the callback mechanism Bacchanalia.

My problem

I am building a simple Node.js application, which receives HTTP requests and sends their JSON body payload combined with their URL arguments as an input to a function. For the sake of learning I do not use any external framework or npm package.

So basically,

curl -i \
     -H "Content-Type: application/json" \
     -X POST \
     -d '{"jsonKey":"jsonValue"}' \
     'http://localhost:8888/y?urlKey=urlValue'

Should call an arbitrary function f with the parameters f({"jsonKey":"jsonValue", "urlKey":"urlValue"}).

Converting the URL args is pretty straightforward and requires no callbacks at all:

/* Parse the URL arguments */
function parseArguments(request) {
    var url_obj = url.parse(request.url, true);
    return url_obj.query;
}

The plot thickens when I try to add the JSON Payload to the returned value:

/* Parse the URL arguments and the JSON payload */
function parseArguments(request) {
    // URL Parameters 
    var arguments_as_object = url.parse(request.url, true).query;

    // JSON Payload, if any
    var body = [];
    request.on('data', function(chunk) {
        body.push(chunk);
    }).on('end', function() {
        body = Buffer.concat(body).toString();
        if (body) {
            body = JSON.parse(body);
            Object.assign(arguments_as_object, body);
        }
        console.log(new Date(), "Arguments with JSON are ", arguments_as_object);
    });

    console.log(new Date(), "Return value is ", arguments_as_object);
    return arguments_as_object;
}

The logs are:

2017-04-16T13:14:47.807Z 'Return value is ' { a: 'b' }
2017-04-16T13:14:47.826Z 'Arguments with JSON are ' { a: 'b', jsonKey: 'jsonValue' }

Unsurprisingly, parseArguments returns before the JSON parse is complete due to the asynchronous nature of javascript.

My question

How can I get the results of the asynchronous call and use them outside the function? Is there a way other than sending a callback function with all its arguments?

What have I tried

  • SFTW.
  • Using a callback instead of a return value. This seems to be the right way, but then I would have to send all the arguments of the callback to parseArguments, which would add a lot of unnecessary variables to its signature.
Community
  • 1
  • 1
Adam Matan
  • 128,757
  • 147
  • 397
  • 562
  • You can't. An async operation won't be completed before your function returns so there's no way to return an async result from your function. You will have to use a callback or a promise. See the [marked duplicate](http://stackoverflow.com/questions/14220321/how-do-i-return-the-response-from-an-asynchronous-call) for various choices. I would recommend returning a promise from your function and then the caller can use `.then()` on the promise to get the async result. – jfriend00 Apr 16 '17 at 13:40

1 Answers1

1

I think you've more or less answered your own question. You have to use a callback instead of returning a value.

You can somewhat address your concern about sending all of the callback arguments to parseArguments by wrapping that function in another function:

function mainFunc = (a, b, c, jsonData) {
...
}    
function parseCb = (jsonData) {
mainFunc(1,2,3, jsonData)
}
parseArguments(request, parseCb)

You can also inject a level of clarity to your asynchronous code by implementing promises, which can be returned and passed around like synchronous variables:

function parseArguments(request) {

return new Promise((resolve, reject) => {
    // URL Parameters 
    var arguments_as_object = url.parse(request.url, true).query;

    // JSON Payload, if any
    var body = [];
    request.on('data', function(chunk) {
        body.push(chunk);
    }).on('end', function() {
        body = Buffer.concat(body).toString();
        if (body) {
            body = JSON.parse(body);
            Object.assign(arguments_as_object, body);
           resolve(arguments_as_object)
        }
        console.log(new Date(), "Arguments with JSON are ", arguments_as_object);
    });

    console.log(new Date(), "Return value is ", arguments_as_object);
    return arguments_as_object;
}
})
Rafael Kennedy
  • 964
  • 5
  • 16