Skip to content

Instantly share code, notes, and snippets.

@dbonillaf
Created July 17, 2015 06:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save dbonillaf/9e5367a6351d522ed6ba to your computer and use it in GitHub Desktop.
Save dbonillaf/9e5367a6351d522ed6ba to your computer and use it in GitHub Desktop.
Intentando entender el paso por referencia en JavaScript
var express = require('express');
var fs = require('fs');
var handlebars = require('handlebars');
var source = new Buffer(10);
// El objeto source no contiene nada
console.log(source.toString('utf-8'));
fs.readFile('home.html', function(error, source){
if(error) {
console.log(error);
} else {
// El objeto source contiene los datos del fichero
console.log(source.toString('utf-8'));
}
});
var app = express();
app.get('/', function (req, res) {
// El objeto source VUELVE a no contener nada
res.send(source.toString('utf-8'));
});
var server = app.listen(3000, function () {
var host = server.address().address;
var port = server.address().port;
console.log('Backend listo para combatir en http://%s:%s', host, port);
});
@dbonillaf
Copy link
Author

En mis primeros y balbuceantes primeros pasos con Node hay una cosa que está consiguiendo que me ROMPA la cabeza ¿Por qué no funciona el paso por referencia en el callback del método readFile? ¿Por qué dentro de la función la variable source tiene un valor y fuera vuelve a tener otro? ¿Me estoy equivocando al referenciar a la variable global como parámetro -no me deja poner "this."-?

@abelgvidal
Copy link

Si lo estoy leyendo bien es un tema de asincronía, fs.readFile es asíncrono. prueba a usar promesas o usar la variante síncrona fs.readFileSync

@cles
Copy link

cles commented Jul 17, 2015

Creo que tienes buena intuición con lo de que no se deberia referenciar la var global como parámetro...

Sin ser ni siquiera programador js (disclaimer!), yo diria que dentro del callback source es el parámetro, pero fuera de ese scope es la variable global. vamos, que no hay copia implícita, ni posibilidad de hacer paso por referencia ahí?...vamos, que si los quieres en la var global, como no los copiemos a mano no sé que decirte.

edit: veo que 3 tios te han dicho lo mismo por twitter ya, llego tarde jaja

@eliOcs
Copy link

eliOcs commented Jul 17, 2015

A diferencia de la mayoría de los lenguajes de programación el ámbito de las variables acaban donde acaba la función. Tienes una variable global que has llamado source y luego tienes un parámetro de la función de callback del readFile que también se llama source. Son variables distintas aunque se llamen igual. Entonces dentro del callback de readFile source hace referencia al parámetro de la función.

/*jslint node: true, maxlen: 80, indent: 4 */
"use strict";

var paco = "paco";

function fun1(paco) {
    console.log(paco);
}

function fun2() {
    var paco = "francisco";

    return function fun3() {
        console.log(paco);
    };
}

console.log(paco);
fun1("kiko");
fun2()();

ese programita te da el siguiente output:

> paco
> kiko
> francisco

Más que de node esto es un tema de javascript, te recomiendo el librito "javascript the good parts" para conocer a fondo javascript y evitarte estos quebraderos de cabeza.

@fsanzv
Copy link

fsanzv commented Jul 17, 2015

Cambia el nombre de la variable source (por ejemplo a data)en el callback de readfile y asigna su valor a source en el else (si no hay error).

@eliOcs
Copy link

eliOcs commented Jul 17, 2015

En cuanto al tema de node, tienes que leer el fichero una vez recibas la petición al servidor. Cuando hayas acabado de leer el fichero puedes devolver el resultado. Tu código funcionaría de la siguiente manera:

/*jslint node: true, maxlen: 80, indent: 4 */
"use strict";

var express = require('express');
var fs = require('fs');
var handlebars = require('handlebars');

var app = express();

app.get('/', function (req, res) {
    fs.readFile('home.html', function (error, source) {
        if (error) {
            console.log(error);
        } else {
            res.send(source.toString('utf-8'));
        }
    });
});

var server = app.listen(3000, function () {
    var host = server.address().address,
        port = server.address().port;

    console.log('Backend listo para combatir en http://%s:%s', host, port);
});

@fsanzv
Copy link

fsanzv commented Jul 17, 2015

En tu ejemplo, source es lo que te devuelve el callback de readfile, no la variable source que has definido fuera.

@Antonio-Laguna
Copy link

Como bien comenta @eliOcs, es un tema de ámbito. Para arreglarlo haz algo así:

fs.readFile('home.html', function(error, fileContent){
  if(error) {
    console.log(error);
  } else {
    source = fileContent;
  }
});

El source recibido en el callback de readFile se puede llamar source o como a ti te plazca, pero solo tiene acceso a esa variable esa función (a no ser que lo saques afuera como con el fragmento que he puesto.

Dicho esto, te recomendaría que usaras readFileSync ya que se podría dar el caso (aunque extraño) de que alguien intentara acceder a / antes de que la lectura haya terminado (complicado pero no imposible).

Ya no recuerdo mucho de express pero creo que no te hace falta hacer todo eso para servir un HTML. Aun así espero haberte ayudado.

@dbonillaf
Copy link
Author

@Beleiros @eliOcs Así es como lo tenía al principio, pero me provocaba escoceres anales asignar dentro de un método valor a una variable que puede que exista o no (si me llevo ese método a otro sitio). Me mola más la solución de @eliOcs de meter la lógica de carga de fichero en el app.get (y oprimizarlo para que sólo lea de disco una vez).

En cualquier caso, creo que lo he entendido... en el caso de Node y del readFile, no estoy referenciando una función sino que la estoy DECLARANDO, por eso tiene su propio scope... lo que ya ME RALLA es que esto TAMPOCO funcione...

var source = new Buffer(10);
// El objeto source no contiene nada
console.log("ANTES DE NADA");
console.log(source.toString('utf-8'));

function funcioncita(error, data){
  if(error) {
    console.log(error);
  } else {
    // El objeto source contiene los datos del fichero
    console.log("DENTRO DEL MÉTODO");
    console.log(data.toString('utf-8'));
  }
};


fs.readFileSync('home.html', funcioncita('', source));
console.log("FUERA DEL MÉTODO");
console.log(source.toString('utf-8'));

var app = express();

app.get('/', function (req, res) {
  // El objeto source VUELVE a no contener nada
  res.send(source.toString('utf-8'));
});

@dbonillaf
Copy link
Author

@beleiros no hace falta todo eso para usar Express, puedes servir HTML a pelo Capello. La idea es cargar plantillas de Handlebars el día de mañana, compilarlas y demás... pero vamos, eso escapa del "scope" de este hilo 😃

@Antonio-Laguna
Copy link

readFileSync devuelve directamente los datos, no tiene callback. En cualquier caso de esta forma:

fs.readFileSync('home.html', funcioncita('', source));

Estás directamente llamando a funcioncita ya que le has añadido los paréntesis. source no se puede pasar de la forma en que tú lo estás intentando. Tendrías que pasar una referencia a la función:

fs.readFileSync('home.html', funcioncita).

Resolviendo tu duda, lo que creo que deberías hacer si vas a usar readFileSync es: source = fs.readFileSync('home.html')

@carlosvillu
Copy link

Hola David,

Lo que necesitas para que esto funcione como esperas, sería crear un ReadStream desde el fichero y pasarlo al WriteStream de la respuesta del servidor:

var express = require('express');
var fs = require('fs');
var path = require( 'path' );

var app = express();

app.get('/', function (req, res) {
  res.set('Content-Type', 'text/html');
  fs.createReadStream( path.join( __dirname + '/index.html' ) ).pipe( res );
});

var server = app.listen(3000, function () {
  var host = server.address().address;
  var port = server.address().port;

  console.log('Backend listo para combatir en http://%s:%s', host, port);
});

Espero que te ayude 😄

p.d: Te dejo un post de mi blog donde hablo sobre como trabajar con ReadStream:
http://carlosvillu.com/aprende-a-usar-readstream-en-nodejs/

@dgaubert
Copy link

var source = new Buffer(10);
// El objeto source no contiene nada
console.log("ANTES DE NADA");
console.log(source.toString('utf-8'));

function funcioncita(error, data){
  if(error) {
    console.log(error);
  } else {
    // El objeto source contiene los datos del fichero
    console.log("DENTRO DEL MÉTODO");
    console.log(data.toString('utf-8'));
  }
};

fs.readFileSync('home.html', funcioncita('', source)); // el callback no se ejecuta NUNCA
// estas ejecutando una función que su resultado se pasa como segundo parametro a 'fs.readFileSync' 
// que según la doc de node, espera que esto sean las opciones (encoding, etc...)
// es decir 'funcioncita' retorna _undefined_ (no return definido), por lo que en realidad estas ejecuutando:
// fs.readFileSync('home.html', undefined); 
// _readFileSync_ es sincrono y usa el metodo de asignacion directa:
// var content = fs.readFileSync('home.html');
// la variable source directamente no estas haciendo nada con ella.

console.log("FUERA DEL MÉTODO");
console.log(source.toString('utf-8'));

var app = express();

app.get('/', function (req, res) {
  // El objeto source VUELVE a no contener nada
  res.send(source.toString('utf-8'));
});

@dbonillaf
Copy link
Author

¡Gracias a todos por la ayuda y por lo aprendido!

Ya me cuesta usar el Sync en algo como Node, se supone que la potencia viene de no utilizarlo NUNCA 😄 Creo que mucho de la "solución" de esto es cambiar el paradigma de pensamiento y crear una función que encapsule la lectura de fichero de forma asíncrona e invocarlo con parametros desde cada app.get... aunque funcionalmente sería una tontería porque, aunque dejara el fichero cargado en memoria, la primera vez que se invocara, por su asincronía, lo más seguro es que la petición HTTP GET no devolviera nada.

Lo más seguro sería hacer la carga de los n ficheros al arrancar la app, una sola vez, de forma síncrona.

@gimenete
Copy link

No hay un paso por referencia del primer source que declaras. Es simplemente otra variable. Es como si en Java haces esto:

class Foo {
  private String source;
  public void bar(String source) {
    // Este source no es el otro source.
    // En Java concretamente podrías usar this.source para acceder al otro
  }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment