Node JS anonymous functions and callbacks

2019-04-08 20:20发布

问题:

Someone please help me. I have been reading a lot of javascript documentation and fiddling with javascript.

I am able to use the functions but I don't quite get this essential syntactic jazz going on here

          var http = require('http');
          http.createServer(function (req, res) {
                 res.writeHead(200, {'Content-Type': 'text/plain'});
                 res.end('Hello World\n');
          }).listen(1337, 'myhost');

          console.log('Server running at http://myhost:1337/');

I cant figure out why its okay to use req and res in the anonymous function above. It's like they live somewhere inside of http. They are not declared anywhere! They are made up variable names in an anonymous function that reference inner objects or something. It's crazy.

How does a callback function like this?

or like

          stream.on('data', function(data){  
                //  use data... i dont know where its coming from 
                //  or how it got there, but use it
          });

If you could post a small example that mimics this process and syntax or explain how these callback functions can work like this or how I can pass these undeclared variables into functions like this I would greatly appreciate it.

A similar example to the answer posted below.

     var b = {                  
           somefunction : function( data ){
                 var x = 100;
                 x = x+1;        // 101

                 return data(x); // returns the callback function data w/ value x
           } 
     };

     b.somefunction(function(foo){
           foo++;                // 101 + 1
           console.log(foo);     // prints 102
     });

回答1:

The main issue is that Javascript is a functional language so you can pass functions as parameters to other functions. In other languages you may have experienced passing a pointer or handle to a function, for example. Consider a simple cases in which you have some math functions:

function add(a,b) {return (a+b)};
function subtract(a,b) {return (a-b)}:

Now I could create a new function:

function longWayAroundTheBarn(num1,num2,theFunc)
{
    // invoke the passed function with the passed parameters
    return theFunc(num1,num2);
}

And call it like this:

console.log(longWayAroundTheBarn(1,2),add);

> 3

Or even like this:

console.log(longWayAroundTheBarn(longWayAroundTheBarn(2,2,subtract),4,add);

> 4

Obviously this would be a silly use callbacks, but you can imagine generally that the ability to 'plug-in' a function this way can be pretty powerful.

Consider if you couldn't pass functions. This might be one way you would implement this:

function reallyLongWayAround(num1,num2,funcName)
{
    if(funcName==='add')
        return add(num1 ,num2);
    else if (funcName === 'subtract')
        return subtract(num1, num2);
}

You can imagine that besides being really tedious code to have to write and maintain, it's not nearly so powerful because the reallyLongWayAround function could only ever call code it knew about. By passing functions, my longWayAroundTheBarn doesn't care if I create new functions and pass it to it. Note that because of weak typing, it doesn't even need to care about the parameters it is passing. Maybe you want to implement something like

 function businessDaysBetween(d1,d2)
 {
     // implementation left as a reader exercise
 };

It would work just fine to call:

longWayAroundTheBarn(new Date(2014,1,15), new Date(2014,1,22),businessDaysBetween)

Returning to the specific case you raised, req and res are not 'local variables' as one answer indicates - they are called parameters or arguments. You aren't passing anything into them. They are being passed to you by the calling function. You could actually call them fred and barney if you wanted, although it would be a terrible idea. The main point is that they will be called populated with request and response objects.

You actually don't even have to have the parameters in your function signature, you could just have a callback as below, and make use of the second parameter passed to your function by reading the arguments array (Note, it's not actually an array but behaves similarly in many respects). This would be a terrible, terrible idea, but again, trying to illustrate the point.

      var http = require('http');
      http.createServer(function () {
             arguments[1].writeHead(200, {'Content-Type': 'text/plain'});
             arguments[1].end('Hello World\n');
      }).listen(1337, 'myhost');


回答2:

The req and res are actually local variables of the anonymous function.
I have an example below similar to the codes posted in your question.

// static class
var HelperClass = {

    "doSomething" : function ( callback ) {
         // do something here, for example ajax call to a server
         var data = ajaxCallFromServer();

         // return the data via callback
         callback ( data );
    };

};


// you codes here calling the HelperClass
// calling .doSomething method with an anonymous function callback 
// that expects 1 parameter.  This parameter is returned by the above 
// code via callback ( data ).  And in the anonymous function 
// i called it as retData, you can call it whatever you like
HelperClass.doSomething(  function ( retData ) {
   // do soemthing with your retData here
});


回答3:

The point is that http.createServer function takes an argument that is not a variable, but a function, if that makes sense. In javascript you can do that. And that function expects arguments that are specified in it's API. You can make it anonymous, like in your example, or declared like below:

function serverFunction(req, res) {
  res.writeHead(200, {'Content-Type': 'text/plain'});
  res.end('Hello World\n');
}

var http = require('http');
http.createServer(serverFunction).listen(1337, 'myhost');

but in the end it does not matter, it just behaves accordingly to what is specified in its the API.



回答4:

You should use the documentation. So for example for createServer, follow this page - http://nodejs.org/api/http.html#http_http_createserver_requestlistener

http.createServer([requestListener])# Returns a new web server object.

The requestListener is a function which is automatically added to the 'request' event.

Then, you check the 'request' event -

Event: 'request'# function (request, response) { }

Emitted each time there is a request. Note that there may be multiple requests per connection (in the case of keep-alive connections). request is an instance of http.IncomingMessage and response is an instance of http.ServerResponse.

Regarding the stream, exactly same. The docs here - http://nodejs.org/api/stream.html#stream_writable_stream

Look for

Event: 'data'