了解Javascript和node.js中的回调

时间:2011-12-18 14:07:01

标签: javascript node.js callback

我是PHP(CodeIgniter& WordPress)开发人员,他最近才想学习其他一些语言。我已经开始学习Ruby(在Rails和Sinatra上),Python(带有Flask框架)和带有node.js的Javascript。

我决定使用这些语言创建我能想到的最基本的应用程序,一个URL扩展器。我已经设法在每种语言中创建一个工作版本,除了node.js和Javascript。

我知道我的问题,我知道它与回调有关。我知道我做得不对。我得到了回调的基本概念,但我无法弄清楚如何修复我创建的这个混乱。

这是我的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();

服务器启动,当发出请求时,它会调用expand函数,该函数返回终端中的扩展URL。我正试图让它在浏览器中打印。

感谢任何帮助。提前谢谢。

3 个答案:

答案 0 :(得分:6)

你犯了一些瑕疵。

你应该重写expand来传递url并传入一个回调函数。任何执行异步操作的函数通常在节点中都有签名(data, callback)。这基本上允许你说我希望这个功能做某事然后告诉我它什么时候完成。

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);    
    }
}

这里回调预计会有(err, data)的签名,几乎所有节点中的回调都有。我们还添加了错误处理,这是必须的。

我们现在更改onRequest以实际调用正确的扩展

function onRequest(request, response) {
    // parse the incoming 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);
    }
}

这里我们添加了错误处理逻辑,并解压缩了从查询字符串扩展的实际URL。

通常doSomething<data, callback<err, result>>的模式在node.js中运行良好。

它与普通阻塞语言中的let result = doSomething<data> mayThrow err完全相同,除非它是异步的。

请注意,将ServerResponse对象传递给函数的替代选项是不受欢迎的,这样做会在expand函数和服务器响应之间创建不必要的硬耦合。

扩展功能应该只扩展一个url并返回扩展的url,它本身没有业务做IO。

Full code

答案 1 :(得分:3)

回调只是一个词来描述我们传递给其他代码以调用其他代码的函数。

在您的示例中,onRequest是一个回调函数,只要收到请求,就会传递给createServer

我认为您遇到的问题是您希望expand()能够访问onRequest函数可以访问的所有相同变量/参数。事实并非如此。

您需要将response对象传递给expand()。由于对expand的调用会为longURL调用创建新的回调http.get,因此它可以访问您传入的response对象。

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.");
}

答案 2 :(得分:0)

您没有将响应作为参数发送到expand函数,并且您在expand()函数可以写任何内容之前调用response.end(),这里是更正的版本:

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();