domingo, 26 de marzo de 2017

¿Cómo instalar y utilizar npm?

Hellooooo :D Es hora de aprender, primero a instalar npm y despues la forma de utilizar npm en el proceso de desarrollo para ayudarnos con tareas que necesitamos realizar recurrentemente.

Primero instalaremos npm:

En windows:

  1. Descargar el instalador de https://nodejs.org/en/download/
  2. Instalar :D
En ubuntu (Linux en general):
  1. Instalamos: $ sudo apt-get install npm
  2. Instalamos n para el manejo de versiones: $ sudo npm install -g n
  3. Ahora utilizaremos n para descargar la última versión de nodejs: $ sudo n latest
Para ver la versión que tenemos instalada, en la consola debemos realizar:

$ npm --version

Ahora a comenzar un proyecto (si no utilizamos -y es necesario responder a algunas preguntas al hacer npm init):

$ npm init -y

Este comando creará un archivo llamado package.json en la raíz del proyecto, con los siguientes campos:

  • name: con el nombre del proyecto
  • version: con la versión del proyecto
  • description: una descripción pequeña del proyecto
  • main: el script principal del proyecto, usualmente index.js
  • scripts: donde podemos almacenar comandos utilizados recurrentemente en el proyecto
  • keywords: algunas palabras clave para referirse al proyecto
  • author: el autor del proyecto
  • license: la licencia del proyecto

Para instalar modulos en nuestro proyecto utilizamos npm install, ahora instalaremos un pequeño servidor de pruebas para desarrollo:

$ npm install live-server --save-dev

Con este comando estamos indicando que queremos que se instale el módulo live-server como una dependencia de desarrollo. Revisando el archivo package json ahora existe un nuevo campo:

"devDependencies": {
    "live-server": "^1.2.0"
  }

Esto nos indica que las una dependencia para el ambiente de desarrollo se llama live-server.
Ahora instalaremos una dependencia de producción que se utiliza bastante:

$ npm install request --save

  "devDependencies": {
    "live-server": "^1.2.0"
  },
  "dependencies": {
    "request": "^2.81.0"
  }

Ahora se ha creado un nuevo campo llamado dependencies, ahí se almacenó la dependencia instalada como una dependencia de producción.

Entonces, existen dos nuevos campos en el package,json que son muuuuy (muuuy :D) útiles:

  • dev-dependencies: que son dependencias que se utilizan solamente en ambientes de desarrollo, como librerias de testing (mocha, tape, etc.), aserciones (chai), transpilers (babel) u otros.
  • dependencies: son las dependencias que son necesarias para el proyecto en el ambiente de producción.

Además, es necesario ver las dependencias que estan desactualizadas en nuestro proyecto, para eso utilizamos:

$ npm outdated

Ejemplo de comando outdated
Como podemos observar, el comando nos muestra la versión actual, la esperada y a última. Idealmente me gusta realizar este proceso dependencia por dependencia para verificar que todo esta corriendo como debe ser.

Sin embargo, también se puede actualizar todo sin necesidad de hacer el proceso manualmente, para ello:

$ npm update --save-dev --save

Tambien podriamos actulizar solamente un paquete y no todos, por ejemplo para actuaizar un paquete con el nombre underscore:

$ npm update underscore

Con esto se actualiza tanto el paquete dentro de la carpeta node_modules como las versiones en el archivo package,json.

También podemos installar paquetes globalmente, para ello se utiliza -g, por ejemplo:

$ npm install mocha -g

Ahora, podemos revisar cuales son los paquetes que se tienen instalados:

$ npm list

Listado de paquetes utilizando npm list


Crear scripts Propios

También podemos crear nuestros propios scripts para iniciar, por ejemplo, para iniciar live server se debe hacer:

$ ./node_modules/.bin/live-server

Pero esta tarea debe realizarse varias veces, entonces la crearemos como una tarea en el archivo package.json:

  "scripts": {
    "start-server": "live-server",
    "test": "echo \"Error: no test specified\" && exit 1"
  },

Ahora para iniciar el servidor:

$ npm run start-server

Entonces, para ejecutar cualquier script del archivo package.json simplemente es necesario utilizar npm run <nombre-script>.

Y eso es todo por ahora, a golpear el teclado!!!! >_<







domingo, 19 de marzo de 2017

Patrones de diseño (Builder Pattern) - ¿Qué hacer cuando se tiene demasiados parámetros en el constructor?

El patrón de construcción (Builder Pattern) es un patrón creacional que se utiliza cuando se tiene demasiados parámetros en un constructor. De acuerdo a Robert C. Martin en su libro Clean Code (lectura sumamente aconsejable), el número ideal de argumentos que se debe enviar a una función es cero, luego un parámetro, luego dos y luego tres. En caso de necesitar una mayor cantidad de parámetros se debe buscar una muy buena justificación para utilizarlos y aún así no utilizarlos :D

Entonces, ¿qué hacemos cuando necesitamos construir una Objeto con bastantes parámetros y se necesita mucha lógica de construcción? Builder Pattern al rescate!!!

Primero veremos un ejemplo de la construcción de una persona de la forma convencional:

function Persona(nombre, apellidos, edad,
                 sexo, altura, tonoDePiel, tipoDeCabello) {
  this.nombre = nombre;
  this.apellidos = apellidos;
  this.edad = edad;
  this.sexo = sexo;
  this.altura = altura;
  this.tonoDePiel = tonoDePiel;
  this.tipoDeCabello = tipoDeCabello;
}

var persona1 = new Persona('Adrian', 'Espinoza', 32,
                            'Masculino', 1.80,
                            '#f3f312', 'lacio');
var persona2 = new Persona('Magda', 'Apaza', 12,
                            'Femenino', 1.60,
                            '#13f3f2', 'lacio');

console.log('+++++++++++++++++++++++++++++++++++++++++');
console.log(persona1);
console.log('+++++++++++++++++++++++++++++++++++++++++');
console.log(persona2);
console.log('+++++++++++++++++++++++++++++++++++++++++');

¿Funciona? Claro que funciona, pero... ¿qué pasaría si quiero agregar un atributo? tendría que modificar tanto Persona como sus creaciones, y ¿si modifico el orden de los atributos? :O se vueve cada vez más complicado el mantenimiento. Ahora veamos el mismo ejemplo con el Builder Pattern:

// Persona builder
function PersonaBuilder() {
  // atributos del builder
  var nombre;
  var apellidos;
  var edad;
  var sexo;
  var altura;
  var tonoDePiel;
  var tipoDeCabello;

  // Objeto persona
  function Persona(personaBuilder) {
    this.nombre = personaBuilder.getNombre();
    this.apellidos = personaBuilder.getApellidos();
    this.edad = personaBuilder.getEdad();
    this.sexo = personaBuilder.getSexo();
    this.altura = personaBuilder.getAltura();
    this.tonoDePiel = personaBuilder.getTonoDePiel();
    this.tipoDeCabello = personaBuilder.getTipoDeCabello();
  }

  // getters, setters y builder
  return {
    setNombre: function (nombre) {
      this.nombre = nombre;
      return this;
    },
    getNombre: function() {
      return this.nombre;
    },
    setApellidos: function (apellidos) {
      this.apellidos = apellidos;
      return this;
    },
    getApellidos: function() {
      return this.apellidos;
    },
    setEdad: function (edad) {
      this.edad = edad;
      return this;
    },
    getEdad: function() {
      return this.edad;
    },
    setSexo: function (sexo) {
      this.sexo = sexo;
      return this;
    },
    getSexo: function() {
      return this.sexo;
    },
    setAltura: function (altura) {
      this.altura = altura;
      return this;
    },
    getAltura: function() {
      return this.altura;
    },
    setTonoDePiel: function (tonoDePiel) {
      this.tonoDePiel = tonoDePiel;
      return this;
    },
    getTonoDePiel: function() {
      return this.tonoDePiel;
    },
    setTipoDeCabello: function (tipoDeCabello) {
      this.tipoDeCabello = tipoDeCabello;
      return this;
    },
    getTipoDeCabello: function() {
      return this.tipoDeCabello;
    },
    build: function () {
      return new Persona(this);
    }
  };
}

// Ahora crear personas ss mucho más simple, además el ordén de los 
// atributos no importa acá
var personaBuilder = new PersonaBuilder();
var persona1 = personaBuilder.setNombre('Jhon')
                            .setApellidos('Perez Apaza')
                            .setEdad(25)
                            .setAltura(1.75)
                            .setSexo('Masculino')
                            .setTonoDePiel('#fa45fc')
                            .setTipoDeCabello('lacio')
                            .build();

var persona2 = personaBuilder.setNombre('Jackie')
                              .setApellidos('Sullivan')
                              .setTipoDeCabello('lacio')
                              .setSexo('Femenino')
                              .setEdad(45)
                              .setAltura(1.50)
                              .setTonoDePiel('#aaa53c')
                              .build();

console.log('+++++++++++++++++++++++++++++++++++++++++');
console.log(persona1);
console.log('+++++++++++++++++++++++++++++++++++++++++');
console.log(persona2);
console.log('+++++++++++++++++++++++++++++++++++++++++');


Ahora es mucho más sencillo realizar cambios y mantenerlos controlados, además, el ordén en que se agregan los atributos ya no importa.

Salida del script anterior

Y eso es todo por ahora. Cabe destacar que no es necesario utilizar cada patrón en todas las ocasiones que se presentan, ya que los patrones agregan complejidad que no siempre es necesario en un proyecto. Este ejemplo puede utilizarse en un proyecto donde sea necesario crear muchas personas y con muchos atributos, pero esto no sucede en todo slos proyectos que estamos utilizando. Para evitar crear un ibjeto con demasiados parámetros también se pueden ir agrupando los parámetros en objetos pequeños para luego construir el objeto completo.

Y eso es todo, recuerden, KISS (Keep It Simple S...) y Keep Coding XD

Nuevas características de ecma6 - Generadores e Iteradores

Hello!!! hoy hablaremos un poco de una nueva característica muy interesante en javascript (ecma6), los generadores.

En términos simples, un generador es una función que genera una serie de valores, con la particularidad de que no todos se generan al mismo tiempo, sino, de acuerdo a lo que se solicite. Es necesario pedir explicitamente valores a un generador, y el generador nos respondera con un nuevo valor o nos notificará que no existen más valores que retornar.

Además, un iterador es un objeto que sabe como acceder a los elementos de una colección uno a uno y que mantiene un registro de su posición en la secuencia :O. Los iteradores proveen el método next() para obtener el siguiente valor de la secuencia. next() devuelve un objeto con las propiedades done y value;

Una característica muy particular de los generadores es que cuando devuelven un valor, no téminan su labor como una función normal, sino que simplemente entran en un estado de suspensión, luego, cuando solicitamos un nuevo valor, el mismo generador reanuda su labor donde se detuvo anteriormente para luego volver a detenerse :O

Dado que siempre es mucho más sencillo entender algo con ejemplos, escribiremos una función que vaya generando números múltiplos de 3 primero y de siete a continuación.

// es necesario utilizar polyfills para que los generadores funcionen
import 'babel-polyfill';

console.log('Iniciando pruebas con generadores');

// agregamos un * para crear un generador
function* generador() {
  for(let i = 1; i <= 3; i++) {
    // utilizamos yield para devolver un valor
    // y pausar la ejecución del generador
    yield i * 3;
  }
  for(let i = 1; i <= 3; i++) {
    yield i * 7;
  }
}

console.log('\nPrimer bucle while\n');

// obtenemos el iterador del generador
const iterator = generador();
let element;
do{
  // obtenemos un valor del iterador
element = iterator.next();
console.log(element); // cuando no hay mas elementos nos detenemos (element.done === true) } while(!element.done); console.log('\nSegundo bucle while\n'); const iterator2 = generador(); let element2; while(!(element2 = iterator2.next()).done ) { console.log(element2); }

Para definir un generador simplemente le agregamos un * a la palabra reservada function (function*). Con esto ya tenemos definido un generador y podemos utilizar la palabra reservada yield para devolver valores. Al llamar a la función no estamos ejecutandola, simplemente estamos creando un iterador, y ya con el iterador podemos utilizar next() para obtener un valor.
Como podemos ver en la imagen, obtenemos los valores del generador correctamente y cuando ya no existen más valores, la propiedad done es true.


Resultado de la ejecución del script

Veamos otro ejemplo interesante, ahora realizaremos la iteración del generador con for-of y revisaremos el operador de desestructuración:

import 'babel-polyfill';

// definimos el generador
function* generador() {
  yield 'Hola ';
  yield 'Mundo ';
  yield 'Con ';
  yield 'Generadores ';
}

console.log('============= Iterando con for of =============');
for (let palabra of generador()) {
  console.log(palabra);
}

console.log('\n============= Con el operador de propagación =============');
// ... permite a al generador expandirse y se almacena en arr
const arr = [...generador()];
console.log(arr)

console.log('\n============= Desestructurando =============');
// propaga el generador en los valores de p1 a p4 (el generador tiene 4 yields)
// como no existe un quinto valor en el generador p5 imprime undefined
const [p1, p2, p3, p4, p5] = [...generador()]
console.log(p1);
console.log(p2);
console.log(p3);
console.log(p4);
// imprimira indefinido porque el generador solamente devuelve cuatro valores
console.log(p5);

El resultado del proceso es:
Resultado del proceso
El operador spread (...) permite al generador ejecutar todos los valores que tenga; además con la desestructuración podemos asignar los valores a variables como se ve en el segundo ejemplo.

Y eso es todo por ahora, espero les haya parecido tan interesante como a mi :p keep coding!!!

martes, 7 de marzo de 2017

Programación asíncrona con Javascript - Parte 2, Promesas (lo básico con ejemplos)

Hola de nuevoooo :D es hora de hablar un poco de las promesas, una promesa es asíncrona por naturaleza, representa un resultado que puede estar disponible ahora, en el futuro o nunca. Una promesa puede resolverse (devolver un resultado) o rechazarse (lanzar un error).

Crear una promesa en javascript es bastante simple, solamente es necesario pasar una función ejecutora que recibe dos parámetros, el primer parámetro es una función que debe llamarse cuando se resuelva la promesa (cuando se quiere devolver un resultado de la operación asíncrona) y el segundo parámetro es la función que se llamará en caso de que la promesa no se cumpla. Tiene la forma new Promise(function(resolver, rechazar){}). Como ejemplo:

// ahora creamos una promesa
var promesa = new Promise(function(resolve, reject) {
  // realizar alguna tarea
  var tareaExitosa = realizarMuchasOperaciones();
  if (tareaExitosa) {
    // resolvemos la promesa en caso de éxito
    resolve('Éxito');
  } else {
    // rechazamos la promesa en caso de error
    reject(new Error('Error al realizar la operación.'));
  }
});

Además, es posible crear promesas ya resueltas o rechazadas:

// para crear una promesa que sea exitosa
Promise.resolve("Promesa exitosa");
// para crear una promesa que sea rechazada
Promise.reject(new Error('Promesa rechazada'));

Cuando se quiere realizar alguna acción o transformación con los resultados de la promesa, se le añade el método then(). then() también nos ayuda a encadenar las promesas una tras otra.

Toda cadena de promesas debe tener el método catch() al final para poder agarrar cualquier error que pudiera presentarse en el proceso.

Por ejemplo, realizaremos una promesa que calculará asíncronamente un número entre 0 y 10, si el número es mayor a 5 la promesa se resuelve correctamente, si el número es menor a 5 la promesa se rechaza.

var promise = new Promise(function(resolve, reject) {
  setTimeout(function(){
    // calculamos un npumero aleatorio entre 0 y 10
    var num = parseInt( Math.random() * 10 );
    if ( num > 5) {
      // la promesa se resuelve si num es mayoa  5
      resolve('Promesa resuelta correctamente, el número es ' + num);
    } else {
      // la promesa se rechaza si numero es menor a cinco
      reject(new Error('Ha ocurrido un error, el número calculado es ' + num));
    }

  }, 2000);
});

// manejamos la promesa
promise.then(function(val){
  // la promesa se ha resuelto
  console.log(val);
}).catch(function(err){
  // la promesa ha sido rechazada
  console.log(err);
});

then() puede encadenarse, y debe devolver un valor (resolve) o debe lanzar un error (el error se pasará a catch).

var promise = new Promise(function(resolve, reject) {
  setTimeout(function(){
    resolve(10);
  }, 2000);
});

promise.then(function(val){
  // retorna un valor, por lo tanto la promesa se resuelve implicitamente
  // y pasará al siguiente then
    return val * 10;
  })
  .then(function(val2) {
    // resolvemos la promesa explicitamente
    // el resultado se pasa al siguiente then
    return Promise.resolve(val2 / 2);
  })
  .then(function(val3) {
    // imprimimos el resultado
    return console.log(val3)
  })
  .catch(function(err){
    // como no hubo error, nunca se imprime
    console.log(err);
  });

En caso de surgir un error (previsto o imprevisto), los siguientes then() no se ejecutan y se pasa directamente al catch().

Por ejemplo, lanzaremos un error en una promesa para ver el ordén en que se ejecutan las operaciones.

var promise = new Promise(function(resolve, reject) {
  setTimeout(function(){
    resolve(10);
  }, 2000);
});

promise.then(function(val){
    console.log('Primer then');
    return val * 10;
  })
  .then(function(val2) {
    console.log('Segundo then');
    if (val2 === 100) {
      throw new Error("Unaceptable, no permitimos el número cien en mi reino");
    }
    return Promise.resolve(val2 / 2);
  })
  .then(function(val3) {
    // imprimimos el resultado
    console.log('Tercer then');
    return console.log(val3)
  })
  .catch(function(err){
    // como no hubo error, nunca se imprime
    console.log('Catch');
    console.log(err);
  });

El resultado que nos muestra es:

Resultado de la ejecución de las promesas
Como podemos observar, se ejecutan el primer y el segundo then(), sin embargo, el tercer then() no se ejecuta pues lanzamos un error, entonces el proceso pasa directamente a catch().

Y eso es todo por ahora, en el siguiente post veremos conceptos más avanzados sobre promesas.

Saludos y keep coding!!!  ( ° _ ° )

Programación asíncrona con Javascript - Parte 1, lo básico (event queue, call stack, event loop, timers y DOM events)

Hola de nuevo, ya que últimamente he estado escribiendo bastante sobre javascript es necesario, para comprender mejor lo que se hace, conocer que es la programación asíncrona (pista: la programación asincrona permite realizar operaciones en segundo plano :D).

Pero primero, ¿porqué es necesaria la programación asíncrona?, el problema es que javascript es single threaded (se ejecuta sobre un solo hilo), lo que imposibilita realizar más de una tarea a la vez. Dado que las tareas se ejecutan una tras otra y que se tiene un entorno single threaded, el hecho de que una tome demasiado tiempo para realizarse evita que las demas tareas se ejecuten y es por esto que los procesos que comunmente tardan (I/O) se ejecutan asíncronamente, para evitar que se tenga que bloquear la realización de otras tareas.

Un ejemplo de un proceso que toma demasiado tiempo en ejecutarse y bloqueara la ejecución del programa hasta que finalice:

function tareaCorta(param) {
  console.log('Esta ejecutandosé la tarea corta ' + param);
}

function tareaLarga(param) {
  console.log('Esta ejecutandosé la tarea larga ' + param);
  for(var i = 0; i <= 5000000000; i++) {
    // bucle que tarda demasiado en ejecutarse
  }
  console.log('Terminó la tarea larga ' + param);
}

tareaCorta(1);
tareaCorta(2);
tareaLarga(3);
tareaCorta(4);
tareaCorta(5);

Y como se puede observar, el programa se detiene para ejecutar la tarea larga (que es simplemente un bucle muuuuuy largo):
Ejemplo de la ejecución del código
No siempre es sencillo comprender el concepto de asincronía, así que diagramaremos el comportamiento en el tiempo del siguiente fragmento de pseudo-código.

TAREA1();
RESULTADO_TAREA_2 = TAREA2();
TAREA3(RESULTADO_TAREA_2);
TAREA4();

Mensajes asíncronos y síncronos
Como se puede observar en la línea de tiempo del gráfico (cada flecha indica la existencia de un hilo), en el estilo síncrono cada tarea debe esperar a que la anterior concluya para realizar otra operación, sin embargo, introduciendo la asincronía si se tiene una operación que consume demasiado tiempo (comunmente I/O), la tarea se ejecuta de manera asíncrona (en otro entorno de ejecución), y luego se puede completar el proceso (usualmente al terminar la tarea asincrona se agrega el callback respectivo al event queue con los resultados de la operación asíncrona). En la imagen se puede observar que dado que la tarea4 no depende de la ejecución de la tarea2, esta se lanza inmediatamente aunque la tarea anterior no haya finalizado.

Para conceptualizar mejor la idea, es necesario estudiar el siguiente gráfico del mismo pseudocógido anterior:
Ejecución asíncrona vs síncrona
Esta vez el tiempo de espera de la petición por la red se ha reducido, y si bien en el primer caso (síncrono) la ejecución no varia demasiado, se puede observar que en el caso de la ejecución asíncrona, aunque la petición por la red ya ha terminado, la tarea3 no puede ejecutarse hasta que la tarea4 haya concluido porque nuestro entorno de ejecución es single threaded. La idea se resume en: todo corre en un hilo distinto excepto nuestro código.

Ahora, algunos conceptos que son de importancia:
  • call stack (pila de llamadas): mantiene un listado de las funciones que están ejecutandose. Si la funcion1 llama a la funcion2, entonces en el callstack está ejecutandose la funcion2 y está en espera en el mismo call stack la funcion1.
  • event queue (cola de eventos): es una cola que mantiene mensajes que referencian a las funciones que estan en espera de ser puestas en el call stack para ser ejecutadas. Las tareas son agregadas a la cola de eventos por web apis que corren en paralelo con el entorno de javascript.
    • web api: agregan tareas al event queue, por ejemplo:
      • timers: programan tareas para ser agregadas al event queue en un determinado tiempo.
      • DOM event handlers: agrega interacciones del usuario, como eventos del mouse o del teclado al event queue.
      • network requests: las peticiones por la red son procesadas asincronamente y devuelven resultados agregando tareas al event queue.
  • event loop (ciclo de eventos): cuando el call stack esta vacio, toma la primera tarea del event queue y la procesa. Las tareas que quedan en la cola (queue) esperan hasta que el call stack esta limpio nuevamente. Este ciclo se llama event loop.

Ejemplo del event queue y el call stack:

function func1(){
  console.log('tarea1');
}

function func2(){
  console.log('tarea1');
}

func1();
func2();

Ejemplo del call stack y del event queue
Ese fue un ejemplo bastante simple, como se puede observar las tareas van ejecutandosé una a una en el call stack hasta que este queda vacio nuevamente. Pero, ¿qué pasaría si las tareas estuvieran relacionadas?

function func1(){
  func2();
}

function func2(){
  console.log('Tarea completadas');
}

func1();

Ejemplo del event queue y call stack
En este último caso podemos ver como se comportan lo seventos cuando estan relacionados, se van agregando al call stack todas las tareas necesarias para que termine la primera tarea que se insertó. En el transcurso del tiempo en que tardan en ejecutarse estas tareas relacionadas, otros eventos pudieron agregar tareas al call stack.

Es posible que las apis web agreguen los resultados de sus procesos como tareas a la cola de eventos (event queue). Estas tareas son definidas por funciones callbacks que se pasan a las apis web.

- callback functions: Son funciones que se pasan como argumentos para ser ejecutadas en un punto futuro del tiempo.

- anonymous callbacks: Son funciones creadas sin nombre. Son útiles cuando el callback solamente necesita ser ejecutado una vez.

Como ejemplo, veamos una función callback con nombre y una función callback anónima:

var numeros = [1, 2, 3, 4, 5];
console.log(numeros);

// callback anónimo, multiplica por dos
var numerosDuplicados = numeros.map(function(valor) {
  return valor * 2;
});
console.log(numerosDuplicados);

function multiplicaPorTres (valor) {
  return valor * 3;
}
// callback con una función que tiene un nombre
var numerosTriplicados= numeros.map(multiplicaPorTres);
console.log(numerosTriplicados);

Timers
Son funciones nativas de javascript que permiten retrasar la ejecución de instrucciones de la manera que necesitemos. El tiempo que necesitamos que se retrasen se calcula asíncronamente.
  • setInterval() se utiliza para programar una tarea recurrente en el tiempo
  • clearInterval() se utiliza para detener una tarea que se creó con setInterval()
  • setTimeout() se  utiliza para programar una tarea en un determinado tiempo
Ahora como ejemplo ejecutaremos la misma tarea del primer ejemplo, pero ahora la primera tarea se retrase 1000 milisegundos (1 segundo).

function tareaCorta(param) {
  console.log('Esta ejecutandosé la tarea corta ' + param);
}

function tareaLarga(param) {
  console.log('Esta ejecutandosé la tarea larga ' + param);
  for(var i = 0; i <= 5000000000; i++) {
    // bucle que tarda demasiado en ejecutarse
  }
  console.log('Terminó la tarea larga ' + param);
}

setTimeout(function(){
  tareaCorta(1)
}, 5000);
tareaCorta(2)
tareaLarga(3);
tareaCorta(4);
tareaCorta(5);

El resultado de la ejecución es el siguiente:

Ejecución con timeout
Como se puede observar, si bien se programó la tarea para que se ejecute en 5 segundos.
El timer funciona correctamente, la tarea se ejecuta cinco segundos despues. Ahora, veamos otro ejemplo un poco mas extraño:

function tareaCorta(param) {
  console.log('Esta ejecutandosé la tarea corta ' + param);
}

function tareaLarga(param) {
  console.log('Esta ejecutandosé la tarea larga ' + param);
  for(var i = 0; i <= 5000000000; i++) {
    // bucle que tarda demasiado en ejecutarse
  }
  console.log('Terminó la tarea larga ' + param);
}

setTimeout(function(){
  tareaCorta(1)
}, 0);
tareaCorta(2)
tareaLarga(3);
tareaCorta(4);
tareaCorta(5);

Como podemos observar, ahora establecimos el timeout a 0, o sea que la tarea se programa para ejecutarse de inmediato. Veamos el resultado:
Resultado de la ejecución
Lo interesante del ejemplo es que la tareaCorta(1) aún se ejecuta al final, pero ¿porqué, si su timeout (tiempo de espera) es cero?, el problema es el orden en el que se agregan las tareas al callstack, veamos (Revisar el diagrama de Ejecución asíncrona vs síncrona que se agrego en uno de los puntos anteriores):

  • Se "programa" la ejecución del timeout en 0 segundos (la tarea ya esta lista para agregarse, pero estamos esperando al siguiente ciclo de eventos)
  • Se agrega al call stack la tareaCorta(2) para su ejecución
  • Se agrega al call stack la tareaLarga(3) para su ejecución
  • Se agrega al call stack la tareaCorta(4) para su ejecución
  • Se agrega al call stack la tareaCorta(5) para su ejecución (ya no existen más elementos para ejecutarse en el call stack así que ahora se agrega la tarea que se programó antes)
  • Se agrega al call stack la tareaCorta(1) para su ejecución

Se programa el timeout, se agregan al call stack las cuatro tareas y luego se agrega la tarea que esta esperando a ser ejecutada por el timeout con 9 milisegundos de espera.

DOM event listeners

Los "oyentes" de eventos del DOM ocurren paralelamente al cógido javascript ejecutado (asíncronamente). Los event listeners detectan un evento y lo agregan al event queue para que el evento sea ejecutado en el futuro. En caso de tener muchos eventos que suceden uno tras otro, todos se agregan en el orden en que ocurren a la cola de eventos (event queue). Algunos eventos que pueden suceder son: keypressed, keyup, mouseenter, mouseleave, etc.

Ahora un ejemplo de el manejo de eventos con javascript super sencillo (lo importante es recordar que los listeners siempre estan escuchando y agregarán el evento al event queue en cualquier momento). Crear en una carpeta el archivo example.html y example.js. El código del archivo example.html es:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Eventos</title>
</head>
<body>
    <div id="div-rojo" style="background-color: red;height:200px;"></div>
    <br />
    <div id="elemento"></div>
    <script src="example.js"></script>
</body>
</html>

Simplemente creamos dos divs, y agregamos el javscript con el nombre "example.js". Ahor el código javascript:

document.getElementById('div-rojo')
  .addEventListener('mouseenter', function(){
    document.getElementById('elemento')
            .innerHTML = 'El mouse ha entrado al div rojo';
});

document.getElementById('div-rojo')
  .addEventListener('mouseleave', function(){
    document.getElementById('elemento')
            .innerHTML = 'El mouse ha salido del div rojo';
});

Con esto, obtenemos el primer div (div-rojo), y le agregamos un listener para el evento mouseenter, cuando el evento suceda, obtenemos el otro div y escribimos el mensaje El mouse ha entrado al div rojo. También, obtenemos el primer div (div-rojo) y le agregamos el evento contrario (mouseleave), pero para que se note el cambio modificamos el mensaje del otro div diciendo que El mouse ha salido del div rojo.

Ejemplo de ejecución (no tomen en cuenta las manchas, es para mantener bajo el tamaño del gif XD)
Y eso es todo por ahora, keep coding!!! :)