Comprendre les callbacks en Javascript et node.js

Je suis un développeur PHP (CodeIgniter & WordPress) de longue date qui ne voulait apprendre que quelques langues. J’ai décidé d’apprendre Ruby (sur Rails et Sinatra), Python (avec framework Flask) et Javascript avec node.js.

J’ai décidé de créer l’application la plus basique à laquelle je puisse penser, un expandeur d’URL, en utilisant chacune de ces langues. J’ai réussi à créer une version de travail dans toutes les langues, à l’exception de node.js et de Javascript.

Je connais un peu mon problème, je sais que c’est lié aux rappels. Je sais que je ne le fais pas bien. Je comprends l’idée de base des rappels, mais je ne peux tout simplement pas comprendre comment réparer le gâchis que j’ai créé.

C’est tout mon 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(); 

Le serveur démarre et, lorsqu’une demande est faite, il appelle la fonction expand qui renvoie l’URL développée dans le terminal. J’essaie de le faire imprimer dans le navigateur.

Toute aide est appréciée. Merci d’avance.

Vous avez fait quelques défauts.

Vous devez réécrire expand pour transmettre l’URL et le rappel. Toute fonction qui fait quelque chose d’asynchrone a généralement la signature (data, callback) dans le noeud. Cela vous permet essentiellement de dire que je veux que cette fonction fasse quelque chose, puis de me dire quand c’est fait.

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

Ici, le rappel devrait avoir la signature (err, data) dont disposent presque tous les rappels du nœud. Nous avons également ajouté la gestion des erreurs, indispensable.

Nous changeons maintenant onRequest pour appeler réellement expand correctement

 function onRequest(request, response) { // parse the incoming url. true flag unpacks the query ssortingng var parsedUrl = url.parse(request.url, true), // extract the queryssortingng 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); } } 

Ici, nous avons ajouté une logique de traitement des erreurs et également décompressé l’URL à développer à partir de la chaîne de requête.

Généralement, le modèle de doSomething> fonctionne très bien dans node.js.

C’est exactement la même chose que let result = doSomething mayThrow err que vous attendez dans vos langages de blocage normaux, sauf que c’est asynchrone.

Notez que l’option alternative ServerResponse transmettre l’object ServerResponse à la fonction est désapprouvée. Vous créez ainsi un couplage dur inutile entre la fonction de développement et la réponse du serveur.

La fonction expand ne doit que développer une URL et renvoyer l’URL développée, elle n’a pas à faire d’IO elle-même.

Code complet

Un rappel est juste un mot pour décrire une fonction que nous passons à un autre code pour que cet autre code soit invoqué.

Dans votre exemple, onRequest est une fonction de rappel qui est transmise à createServer pour être invoquée chaque fois qu’une demande est reçue.

Je pense que le problème que vous rencontrez est que vous vous attendez à ce que expand() ait access à toutes les mêmes variables / parameters auxquels la fonction onRequest a access. Ce n’est pas le cas

Vous devez transmettre l’object de response à expand() . Comme l’appel à expand crée un nouveau rappel longURL pour l’appel http.get , il aura access à l’object de response que vous avez transmis.

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

Vous n’envoyiez pas la réponse en tant que paramètre à la fonction expand et vous appeliez également response.end () avant que la fonction expand () puisse écrire quoi que ce soit, voici la version corrigée:

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