2011-12-18 61 views
2

我是一個很長時間的PHP(CodeIgniter & WordPress)開發人員,最近纔想學習其他幾種語言。我開始學習Ruby(Rails和Sinatra),Python(w/Flask框架)和Javascript以及node.js。瞭解Javascript和node.js中的回調

我決定創建一個我能想到的最基本的應用程序,一個使用這些語言的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(); 

服務器開始,當一個請求時,它調用擴展功能,它返回擴展URL的終端。我試圖讓它在瀏覽器中打印。

任何幫助表示讚賞。提前致謝。

回答

6

你已經做出了一些缺陷。

您應該重寫擴展以傳遞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本身。

Full code

+0

如果不輸入url參數,則此代碼失敗。在繼續調用'expand(urlToExpand,writeResponse);'之前,您需要檢查是否定義了'parsedUrl.query.url'; – Wavesailor 2013-08-27 11:51:37

3

回調只是一個詞,用於描述我們傳遞給其他代碼調用的其他代碼的函數。

在你的例子中,onRequest是一個回調函數,當接收到一個請求時,這個回調函數被傳遞給createServer

我認爲你遇到的問題是,你期望expand()有權訪問onRequest函數有權訪問的所有相同變量/參數。情況並非如此。

您需要將response對象傳遞給expand()。因爲調用expandhttp.get調用創建一個新的回調longURL,將有機會獲得您在傳遞的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

你沒有發送響應作爲參數來擴展功能和你也分別致電展開()函數到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();