最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

Understanding callbacks in Javascript and node.js - Stack Overflow

programmeradmin1浏览0评论

I'm a long time PHP (CodeIgniter & WordPress) developer that only recently wanted to learn a few other languages. I've set out to learn Ruby (on Rails, and Sinatra), Python (w/ Flask framework) and Javascript with node.js.

I decided to create the most basic application I can think of, a URL expander, using each of these languages. I have managed to create a working version in every language, except node.js and Javascript.

I kinda know my problem, I know it is related to callbacks. I know I'm not doing it right. I get the basic idea of callbacks, but I just cannot figure out how to fix this mess I have created.

This is my whole code:

var http = require('http');
var url = require('url');
function expand() {
    var short = url.parse('');
    var options = {
        host: short.hostname,
        port: 80,
        path: short.pathname
    };
    function longURL(response) {
        console.log(response.headers.location);
    }
    http.get(options, longURL);
}

function start() {
    function onRequest(request, response) {
        console.log("Request received.");
        response.writeHead(200, {
            "Content-Type": "text/plain"
        });
        response.write("Hello World");
        expand();
        response.end();
    }
    http.createServer(onRequest).listen(8888);
    console.log("Server has started.");
}
start();

The server starts, and when a request is made, it calls the expand function which returns the expanded URL in the terminal. I'm trying to get it to print in the browser.

Any help is appreciated. Thanks in advance.

I'm a long time PHP (CodeIgniter & WordPress) developer that only recently wanted to learn a few other languages. I've set out to learn Ruby (on Rails, and Sinatra), Python (w/ Flask framework) and Javascript with node.js.

I decided to create the most basic application I can think of, a URL expander, using each of these languages. I have managed to create a working version in every language, except node.js and Javascript.

I kinda know my problem, I know it is related to callbacks. I know I'm not doing it right. I get the basic idea of callbacks, but I just cannot figure out how to fix this mess I have created.

This is my whole code:

var http = require('http');
var url = require('url');
function expand() {
    var short = url.parse('http://t.co/wbDrgquZ');
    var options = {
        host: short.hostname,
        port: 80,
        path: short.pathname
    };
    function longURL(response) {
        console.log(response.headers.location);
    }
    http.get(options, longURL);
}

function start() {
    function onRequest(request, response) {
        console.log("Request received.");
        response.writeHead(200, {
            "Content-Type": "text/plain"
        });
        response.write("Hello World");
        expand();
        response.end();
    }
    http.createServer(onRequest).listen(8888);
    console.log("Server has started.");
}
start();

The server starts, and when a request is made, it calls the expand function which returns the expanded URL in the terminal. I'm trying to get it to print in the browser.

Any help is appreciated. Thanks in advance.

Share Improve this question edited Dec 18, 2011 at 14:23 RightSaidFred 11.3k37 silver badges36 bronze badges asked Dec 18, 2011 at 14:07 Cem K.Cem K. 3383 silver badges8 bronze badges
Add a ment  | 

3 Answers 3

Reset to default 6

You've made a few flaws.

You should rewrite expand to pass the url in and pass a callback in. Any function that does anything asynchronous generally has the signature (data, callback) in node. This basically allows you to say I want this function to do something then tell me when it's done.

function expand(urlToParse, callback) {
    // note we pass in the url this time
    var short = url.parse(urlToParse);
    var options = {
        host: short.hostname,
        port: 80,
        path: short.pathname
    };
    // note we store the clientRequest object temporarily
    var clientRequest = http.get(options, extractRealURL);

    // Always attach the error handler and forward any errors
    clientRequest.on("error", forwardError);

    function extractRealURL(res) {
        callback(null, res.headers.location);    
    }

    function forwardError(error) {
        callback(err);    
    }
}

Here the callback is expected to have the signature of (err, data) which almost all callbacks in node have. We've also added error handling which is a must.

We now change onRequest to actually call expand properly

function onRequest(request, response) {
    // parse the ining url. true flag unpacks the query string
    var parsedUrl = url.parse(request.url, true),
        // extract the querystring url. 
        // http://localhost:8888/?url=http://t.co/wbDrgquZ
        urlToExpand = parsedUrl.query.url;

    // call expand with the url and a callback
    expand(urlToExpand, writeResponse);

    function writeResponse(error, newUrl) {
        // handle the error case properly
        if (error) {
            response.writeHead(500, { 'Content-Type': 'text/plain'});
            // early return to avoid an else block
            return response.end(error.message);
        }
        response.writeHead(200, { 'Content-Type': 'text/plain'});
        // write the new url to the response
        response.end(newUrl);
    }
}

Here we have added error handling logic and also unpacked the actual url to expand from the query string.

Generally the pattern of doSomething<data, callback<err, result>> works very well in node.js.

It's the exact same as let result = doSomething<data> mayThrow err that you expect in your normal blocking languages except it's asynchronous.

Note that the alternative option of passing the ServerResponse object into the function is frowned upon, by doing so your creating unnecessary hard coupling between the expand function and the server response.

The expand function should only expand an url and return the expanded url, it has no business doing IO itself.

Full code

A callback is just a word to describe a function that we pass to some other code for that other code to invoke.

In your example, onRequest is a callback function that gets passed to createServer to be invoked whenever a request is received.

I think the issue you're having is that you're expecting expand() to have access to all the same variables/parameters that the onRequest function has access to. This isn't the case.

You need pass the response object to expand(). Because the call to expand creates a new callback longURL for the http.get call, it will have access to the response object that you passed in.

function expand( resp ) {
     // receive the original response object, and end the response when ready
    var short = url.parse('http://t.co/wbDrgquZ');
    var options = {
        host: short.hostname,
        port: 80,
        path: short.pathname
    };
    function longURL( response ) { 
        console.log(response.headers.location);
        resp.end( response.headers.location ); // end the original response
    }
    http.get(options, longURL);
}

function start() {
    function onRequest(request, response) {
        console.log("Request received.");
        response.writeHead(200, {
            "Content-Type": "text/plain"
        });
        response.write("Hello World");
        expand( response ); // pass this response object to expand
    }
    http.createServer(onRequest).listen(8888);
    console.log("Server has started.");
}

You weren't sending the response as a parameter to the expand function and also you were calling response.end() before the expand() function could write anything, here's the corrected version:

var http = require('http');
var url = require('url');

function expand(res) {

  var short = url.parse('http://t.co/wbDrgquZ');

  var options = {
    host: short.hostname,
    port: 80,
    path: short.pathname
  };

  function longURL(response){
    console.log(response.headers.location);
    res.end("<br />" + response.headers.location);
  }

  http.get(options, longURL);

}


function start() {
  function onRequest(request, response) {
    console.log("Request received.");
    response.writeHead(200, {"Content-Type": "text/html"});
    response.write("Hello World");

    expand(response);
  }

  http.createServer(onRequest).listen(8888);
  console.log("Server has started.");
}


start();
发布评论

评论列表(0)

  1. 暂无评论