Callbacks y funciones asíncronas, el estilo Node.js
El estilo nodejs
Yo pensaba que sabía -algo- de javascript hasta que descubrí Node.js :P. En fin, espero ser siempre
un aprendiz. Conforme te vas asomando a nuevas tecnologías tratas de hacer las cosas correctamente y
como recién llegado aplicas lo de donde fueres haz lo que vieres. ¿Existe algún documento con
las convenciones de Node.js? Sí, Node.js tiene una guía de estilo aunque no es oficial. Pero ojo porque como se suele decir old habits are hard to break, y habrá cosas que igual te cuesten un poco, cosas tan simples como eso, las comillas simples/dobles, en fin... supongo que mucho de esa guía es debatible pero es lo que hay.
De lo que si se habla es del preferred Node.js style que supongo que se resume seguir a los pros y a la comunidad en general. Cómo dice Felix, el autor de esa guía algunos de estos temas son religiosos. Todavía me parto con el inicio de la guía:
Let's start with the religious problems first. Our benevolent dictator (el pro de turno) has chosen 2 space indention for the node core, so you would do well to follow his choice..
Toda la guía se jaja de las discusiones y de los talibanes (también la guía tiene un tono ultra-ortodoxo) lo cual la hace muy simpática, a mi parecer. Otra perla:
Do not extend the prototypes of any objects, especially native ones. There is a special place in hell waiting for you if you don't obey this rule.
En fin, este tipo de polémicas entre programadores siempre han provocado oceanos de sangre...
No es solo cuestión de comillas y de camel case
Una de las cosas que chocan un poco al ir entrando en nodejs y en el javascript actual que todo lo invade es el tema de las llamadas asíncronas. Se estila el llamar a un método y establecer un callback para cuando ese método termine, cosa que por ejemplo se hace en el API de indexedDB. Y la cosa se puede complicar ya que puedes encadenar llamadas de callbacks cosa que puede llevarte a lo que se debe llamar callback spaghetti.
En el caso de que te vayas a meter en esos líos que sepas que en node dispones de módulos (Step, Async) que te permiten encadenar llamadas a funciones y además aplicar patrones asíncronos para que esas llamadas se hagan en cascada, en paralelo, en serie,... etc. Por ahora vamos a ver algo tan simple como dar soporte al callback.
Funciones de callback
Una de las principales recomendaciones de la guía de estilo de Node.js es que se utilicen funciones de callback ya que en definitiva las llamadas asíncronas son el alma de Node. En el siguiente ejemplo juego con un par de cosas: por una lado un método que puede recibir más de un parámetro y se preocupa de controlarlo y de paso muestra cómo controlar los tipos. Y por otro muestra cómo crear una función y que tenga soporte para un callback. Es tan simple como controlar que el último parámetro es de tipo función y nos debemos preocupar de dos cosas:
- Si hay error, el marrón se lo pasamos al callback.
- Si no hay error, lo último que hacemos en la función es llamar al callback pasándole el resultado.
/** * callbacks.js * variable arguments in function calls and * function with callbacks * @author Pello Xabier Altadill Izura * @greetz 4 u */ /* * example * shows the use of variable arguments and how * to deal with them. */ function example (param1, param2) { console.log('example called. arguments: ' + arguments.length); for (i = 0; i < arguments.length;i++) { console.log('param ' + i + ': '+ arguments[i] + ". type: " + typeof(arguments[i])); } // check for argument types: // Check if first argument is an integer if (typeof(param1) !== 'number') { console.log('param1 is not a number: '); } // Check if second argument is a String if (typeof(param2) !== 'string') { console.log('param2 is not a string:'); } console.log("All arguments are ok\n"); } example(); example(5); example(3,'6'); example('juan','perico','ejemplo'); /** * sumWithCallback * will sum two values and call the callback if any. */ function sumWithCallback(a,b) { // Is there any callback? var possibleCallback = arguments[arguments.length - 1]; callback = (typeof(possibleCallback) == 'function' ? possibleCallback : null); // we mark the position of the last numeric argument depending of callback function presence var last = (callback == null)?arguments.length:arguments.length-1; // minium arguments: 1 if (arguments.length < 1) { return callback(new Error("Please give me one argument.")); } var result = 0; for (i=0;i<last;i++) { if (typeof(arguments[i]) !== 'number') { return callback(new Error('Argument ' + i + ' n(ot a number: ' + arguments[i])); } else { result += arguments[i]; } } if (callback == null) return result else return callback(null,result); } console.log(sumWithCallback(4,5,2)); // Now we call the same function with a callback var result = sumWithCallback(600,66, function (err,data) { // throw error if (err) throw err; console.log("result of operation: " + data); return data; }); console.log(result);