Понимание обратных вызовов в Javascript и node.js - PullRequest
2 голосов
/ 18 декабря 2011

Я давний разработчик PHP (CodeIgniter & WordPress), который только недавно хотел выучить несколько других языков.Я начал изучать Ruby (на Rails и Sinatra), Python (с фреймворком Flask) и Javascript с помощью node.js.

Я решил создать самое простое приложение, которое я могу себе представить, - расширитель URL, используя каждый из этих языков.Мне удалось создать рабочую версию на всех языках, кроме node.js и Javascript.

Я знаю свою проблему, знаю, что она связана с обратными вызовами.Я знаю, что делаю это неправильно.Я понял основную идею обратных вызовов, но я просто не могу понять, как исправить созданный мною беспорядок.

Это мой весь код :

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

Сервер запускается, и когда делается запрос, он вызывает функцию расширения, которая возвращает расширенный URL-адрес в терминале.Я пытаюсь заставить его печатать в браузере.

Любая помощь приветствуется.Заранее спасибо.

Ответы [ 3 ]

6 голосов
/ 18 декабря 2011

Вы сделали несколько недостатков.

Вы должны переписать раскрытие, чтобы передать 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 в функцию не одобряется, тем самым создавая ненужную жесткую связь между функцией расширения и ответом сервера.

Функция раскрытия должна только расширять URL-адрес и возвращать расширенный URL-адрес, не имеет никакого отношения к IO.

Полный код

3 голосов
/ 18 декабря 2011

Обратный вызов - это просто слово для описания функции, которую мы передаем другому коду для вызова этого другого кода.

В вашем примере onRequest - это функция обратного вызова, которая передается * 1004.* для вызова при получении запроса.

Я думаю, что проблема, с которой вы столкнулись, заключается в том, что вы ожидаете, что 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.");
}
0 голосов
/ 18 декабря 2011

Вы не отправляли ответ как параметр в функцию расширения, а также вызывали response.end () до того, как функция expand () могла написать что-либо, вот исправленная версия:

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();
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...