0% encontró este documento útil (0 votos)
18 vistas34 páginas

Sprint - 10 1

1) Megan se reúne con Carmela Mitsouko, una ingeniera que ha creado un prototipo de dispositivo que traduce los balbuceos de los niños en peticiones que los padres pueden entender. 2) El sprint se centra en aprender a trabajar con APIs asíncronas para acceder a datos externos y agregar interactividad a las páginas web. 3) Los estudiantes aprenderán sobre código asíncrono, el uso de APIs y prototipos de objetos.

Cargado por

Jorge Sepulveda
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como DOCX, PDF, TXT o lee en línea desde Scribd
0% encontró este documento útil (0 votos)
18 vistas34 páginas

Sprint - 10 1

1) Megan se reúne con Carmela Mitsouko, una ingeniera que ha creado un prototipo de dispositivo que traduce los balbuceos de los niños en peticiones que los padres pueden entender. 2) El sprint se centra en aprender a trabajar con APIs asíncronas para acceder a datos externos y agregar interactividad a las páginas web. 3) Los estudiantes aprenderán sobre código asíncrono, el uso de APIs y prototipos de objetos.

Cargado por

Jorge Sepulveda
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como DOCX, PDF, TXT o lee en línea desde Scribd
Está en la página 1/ 34

Sprint 10

https://youtu.be/UKFG-S_cXWQ

Megan había estado demasiado ocupada haciendo malabares con sus estudios de
desarrollo web y cuidando de un niño pequeño como para visitar el nuevo centro
comunitario. Pero hoy es el día: Megan ha sido citada en el centro. Se va a reunir
con Carmela Mitsouko, una talentosa ingeniera que ha creado un prototipo de un
dispositivo innovador que traduce los balbuceos de los niños pequeños en
peticiones que los padres pueden entender. Si funciona, el dispositivo le ahorrará
a Megan un montón de tiempo que podrá utilizar para crear páginas web.
El arte de trabajar con APIs, que será lo que empezarás a dominar en este sprint,
en realidad se parece bastante a la invención de Carmela. Aprenderás a
comunicarte con un servidor enviando solicitudes HTTP desde el front-end, para
procesar los datos que llegan desde el back-end y a mostrar esos datos a los
usuarios.
¿De qué trata este sprint?

Has llegado a uno de los conceptos más importantes de JavaScript: la asincronía.


Este sprint en ciertos puntos te parecerá difícil, y puede que no lo entiendas todo
al momento. No obstante, a medida que trabajes en los ejercicios y en el proyecto
de este capítulo, acabarás de comprenderlo.
Tu duro trabajo se verá recompensado, ya que adquirirás una nueva e importante
habilidad: la capacidad de trabajar con interfaces de programación de
aplicaciones, también conocidas como API. Esta tecnología te permitirá acceder a
datos externos, añadiendo así una capa extra de interactividad a tus páginas web.
Hoy en día, la mayoría de las páginas web modernas hacen uso de las API.
Además, ahora que hemos pasado el ecuador del programa, vamos a empezar a
centrarnos más en la preparación de tu futura carrera como desarrollador web. A
partir de ahora, cada sprint contendrá un capítulo sobre un tema que puede
aparecerte en las entrevistas de trabajo. Son como cualquier otro capítulo del
programa, pero hemos añadido a los títulos "Prepararse para una entrevista" para
que sepas que es algo especialmente útil para ayudarte a conseguir ese trabajo
soñado. En este sprint, ese capítulo se centrará en los prototipos de objetos.
Qué aprenderás
 JavaScript asíncrono
 Trabajar con APIs
 Prototipos de objetos: Prepararse para una entrevista

¿Qué habilidades obtendrás?

Aprenderás a escribir aplicaciones modernas con código asíncrono. También


aprenderás a utilizar una API para obtener datos del servidor. Por último,
aprenderás más sobre cómo funciona JavaScript por dentro.

https://youtu.be/BfV6eqmOMI8

Operaciones asíncronas

Megan es la prueba de que no todas las heroínas llevan capa. Su capacidad para
hacer varias tareas a la vez en medio del caos es algo increíble. Compone música
rock mientras mece a la pequeña Payton, y si tiene la suerte de que su hija
pequeña se duerma durante unos minutos, aprovecha ese tiempo para escribir y
probar un par de funciones para sus proyectos de Practicum, intentando no
perturbar la breve siesta de su hija.
Y es verdad que Javascript no puede hacerse cargo de los niños (los nodos hijos
no cuentan). Sin embargo, JavaScript y Megan tienen algo en común; ambos
pueden realizar tareas de forma no lineal. Esto se conoce como asincronía.
¿Qué es el código síncrono?

El código de JavaScript es fundamentalmente una serie de sentencias y


expresiones, como la declaración de variables, la adición de elementos a un array
o la suma de dos números. Cada línea de código suele estar compuesta por una
única sentencia o expresión. Sin embargo, a menudo se puede ver más de una en
una sola línea:
Copiar códigoJAVASCRIPT

const a = "Asignar un string a una variable es una instrucción";


// La siguiente línea contiene varios comandos, cada uno separado por un punto y coma.
for (let i = 0; i <= 10; i++) {
console.log("Registrar datos en la consola es también una instrucción");
}

En cuanto a las operaciones, pueden ser síncronas o asíncronas. Veamos cada una
de ellas con más detalle.
Pongamos de ejemplo una tarea que se da a menudo en las entrevistas de trabajo
y que requiere que el candidato escriba una función que devuelva los números de
la secuencia de Fibonacci:
Copiar códigoJAVASCRIPT

function computeFibonacciElement(n) {
if (n <= 2) return 1;

const f = [0, 1, 1];

for (let i = 3; i <= n; i++) {


f[i] = f[i - 1] + f[i - 2];
}

return f[f.length - 1];


}

Ahora vamos a calcular el número mil de la secuencia:


Copiar códigoJAVASCRIPT

console.log(computeFibonacciElement(1000)); // 4.346655768693743e+208

Nos puede parecer que el motor JS calculó este resultado al instante, pero en
realidad tardó unos 30 milisegundos. El resultado solo se registra en la consola
una vez realizado el cálculo. Este retraso puede no parecer mucho, pero puede
tener un efecto significativo dependiendo de las circunstancias.
En nuestro caso, no importa porque no tiene sentido registrar el resultado en la
consola antes de que se haya calculado, de tal forma que el orden de los eventos
tiene sentido. Sin embargo, si nuestro código tuviera otras tareas que realizar,
éstas tendrían que estar en cola esperando a ser ejecutadas hasta que
transcurrieran los 30 milisegundos, lo que en algunos casos puede afectar a la
ejecución de nuestro programa.
Por defecto, las operaciones de JavaScript se ejecutan de forma lineal:
Como resultado, obtenemos una especie de cola de operaciones, en la que se
impide la ejecución de cada fragmento de código hasta que se haya ejecutado
todo lo que le precede. Este tipo de comportamiento se denomina código
síncrono.
Podemos comparar el código síncrono con darnos una ducha. En primer lugar,
tienes que meterte en la ducha y contemplar el sentido de la vida durante un
rato. Luego, te enjabonas. Y una vez hecho esto, aclaras el jabón. No puedes pasar
al siguiente paso hasta no haber completado el anterior.
Del mismo modo, el código síncrono ejecuta cada comando siguiendo una
secuencia. Solo avanza al siguiente paso una vez que el anterior esté completo.
¿Qué es el código asíncrono?

Hay casos en los que las cosas no se ponen en cola de forma estrictamente lineal
como se ha descrito anteriormente. Al terminar de ducharte, vas a prepararte el
desayuno. Imaginémonos que quieres prepararte una tortilla y una buena taza de
café. Así que pones la sartén en el fuego, añades un poco de aceite y rompes unos
huevos. Muy bien. Mientras se cocina la tortilla, puedes empezar a prepararte un
café.
No es necesario esperar a que la tortilla termine de cocinarse para hacer esto,
aunque hay que realizar los pasos de cada tarea individual siguiendo una
secuencia. No se puede sacar la sartén del fuego antes de que la tortilla esté lista;
y no se puede encender la cafetera antes de haber puesto un poco de café en el
filtro y haber llenado la máquina de agua.
Este tipo de procesos pueden compararse con los métodos que toman callbacks
como argumento. Por ejemplo, piensa en el método addEventListener(), que dice
que cuando ocurre un evento (la tortilla está lista), algún otro evento (¡saca la
sartén del fuego antes de que se quemen los huevos!) debe dispararse:
Copiar códigoJAVASCRIPT

// Retirar del fuego


function removeFromStove(evt) {
evt.target.remove();
}

const panWithOmelet = document.querySelector("#sarten"); // la sartén con tu tortilla

panWithOmelet.addEventListener("click", removeFromStove); // cuando se hace clic en panWithOmelet,


será apartado de su padre, que es el fuego

// más código

El proceso de hacer una tortilla sigue siendo sincrónico porque no puedes


comerte la tortilla antes de que esté cocinada. Sin embargo, esta configuración te
permite ejecutar otro código (como hacer café) mientras esperas a que se cocine
tu tortilla, haciendo por lo tanto que tu código en su totalidad funcione de forma
asíncrona. Por ello, el código ya no es estrictamente lineal y puede visualizarse de
la siguiente forma:
¿Por qué necesitamos código asíncrono?

El motor de JavaScript no es lo único que interactúa con una página web. Crear la
experiencia que recibirá el usuario también incluye el hardware del ordenador,
como una CPU, un teclado y una tarjeta de red. Como el motor no puede
comunicarse con estos dispositivos directamente, necesita una interfaz que sirva
de puente entre JavaScript y el hardware externo.
Este tipo de interfaz se conoce como API (abreviatura inglesa de Application
Programming Interface o interfaz de programación de aplicaciones), que es un
conjunto de comandos que rigen la forma en que el motor JS interactúa con el
hardware del PC. Esto nos permite especificar el comportamiento de nuestra
página web en función de la entrada de diferentes tipos de dispositivos.
Así es como el motor de JavaScript interactúa con los dispositivos a través de una
API:
 Encuentra un trozo de código que requiere algún tipo de interferencia de
un dispositivo físico para ser ejecutado. Por ejemplo, podría ser un
controlador de eventos que detecte un clic del ratón.
 A continuación, el motor se remite a la API como si dijera: "Estoy
esperando a que hagan clic sobre mi. Si esto sucede, me darás el objeto
event y me darás la señal de que debo ejecutar este código".
 Cuando se produce el evento de clic, la API recoge todos los datos sobre el
objeto event y los entrega al motor JS.
 Cuando el motor recibe el objeto event, ejecuta el código en el callback.

Una API puede contener instrucciones para cualquier tipo de evento, como pulsar
el botón de algún dispositivo Bluetooth, recibir una señal de una tarjeta de red o
algún movimiento del cursor. En cualquier caso, la lógica básica del
funcionamiento de la API es la misma. El motor JS se comunica con la API y espera
que esta responda con ciertos datos. Cuando se reciben estos datos, el motor
ejecuta el código correspondiente.
Por lo tanto, necesitamos código asíncrono para que nuestra página web sea
capaz de interactuar con el usuario, así como con el dispositivo que se utiliza para
acceder a la página, al mismo tiempo que permite que diferentes procesos tengan
lugar en paralelo. Hay diferentes maneras de trabajar con código asíncrono. En las
próximas unidades, hablaremos de cómo podemos hacerlo utilizando funciones
de callback.

Callbacks

En la unidad anterior, tratamos el tema sobre los tipos de código síncrono y


asíncrono. Recapitulemos rápidamente lo que sabemos al respecto. El código
síncrono es el que realiza las operaciones de una en una y en secuencia. Mientras
que el código asíncrono, permite que las operaciones se ejecuten sin ese orden e
incluso que se espere a que se complete otro código. A veces, los bloques de
código sin orden pueden incluso ejecutarse simultáneamente. Por ejemplo,
alguna función puede ejecutarse cuando un usuario hace clic en un elemento de
la página, mientras que otra se ejecuta después de recibir una respuesta del
servidor.
Una forma de trabajar con código asíncrono es utilizar callbacks. Esta forma de
hacerlo será el punto principal de esta unidad.
Callbacks: Síncronos y asíncronos

Antes de empezar a hablar sobre los tipos de callbacks, vamos a refrescar la


memoria sobre lo que son los callbacks en primer lugar. Un callback es una
función que se pasa a través de otra función, desde la que se llama
posteriormente.
Ya te has encontrado con callbacks en algunos de los capítulos anteriores. Los
usamos cuando trabajamos con arrays, pasándolos a los métodos forEach(),
map() y reduce(), y también los pasamos al método addEventListener() en el
capítulo sobre control de eventos. El uso de callbacks no hace que tu código sea
automáticamente asíncrono, pero cualquier callback puede ser llamado tanto de
forma síncrona como asíncrona.
Empecemos con una llamada a una función de callback síncrona. En el capítulo
sobre arrays, hablamos del método forEach(), que itera sobre los elementos de
un array y ejecuta un callback en cada uno de ellos:
Copiar códigoJAVASCRIPT

const tweets = [
"algún hilo raro",
"una respuesta al tweet de Elon Musk",
"reacción a las noticias de última hora"
];

tweets.forEach(function (tweet) {
console.log(tweet);
});

Un callback puede ser una función anónima. También se puede declarar de forma
independiente y pasar por el nombre:
Copiar códigoJAVASCRIPT

const tweets = [
"algún hilo raro",
"una respuesta al tweet de Elon Musk",
"reacción a las noticias de última hora"
];

function consoleTweet(tweet) {
console.log(tweet);
}

tweets.forEach(consoleTweet);

Ahora, vamos a describir la funcionalidad para renderizar tweets en una página.


Esto se hará mediante una función que toma como argumentos el texto del tweet
y el selector del contenedor donde irá el texto:
Copiar códigoJAVASCRIPT

function insertTweet(tweet, containerSelector) {


const tweetContainer = document.querySelector(containerSelector);
tweetContainer.textContent = tweet;
}

¿Pero qué pasa si algo sale mal? Digamos que no podemos encontrar el
contenedor por su selector. Para tener en cuenta esta posibilidad, tenemos que
añadir una comprobación:
Copiar códigoJAVASCRIPT

function insertTweet(tweet, containerSelector) {


const tweetContainer = document.querySelector(containerSelector);
// comprobar si todo está bien con el contenedor de tweets
if (!tweetContainer) {
console.log("El contenedor de tweets no se encuentra");
/* detener la ejecución de la función
para evitar que el código posterior dé lugar a un error */
return;
}
tweetContainer.textContent = tweet;
}

Si se produce un error, la consola nos informará de ello, lo cual es estupendo.


Pero digamos que en lugar de mostrar el mensaje de error cuando se produce un
error, queremos crear un nuevo contenedor y añadirle el tweet. Para conseguirlo,
podemos reescribir nuestro código de la siguiente manera:
Copiar códigoJAVASCRIPT

function insertTweet(tweet, containerSelector) {


const tweetContainer = document.querySelector(containerSelector);
// si no se encuentra el contenedor adecuado, vamos a crearlo
if (!tweetContainer) {
const newTweetContainer = document.createElement("div");
newTweetContainer.textContent = tweet;
document.body.append(newTweetContainer);
return;
} tweetContainer.textContent = tweet;
}

No es muy conveniente cambiar el código de esta función cada vez que


necesitamos cambiar un mensaje de error, así que vamos a mover el código de
error a una función separada:
Copiar códigoJAVASCRIPT

function handleError(tweet) {
const newTweetContainer = document.createElement("div");
newTweetContainer.textContent = tweet;
document.body.append(newTweetContainer);
}
// añade un tercer parámetro, que es un callback
function insertTweet(tweet, containerSelector, callback) {
const tweetContainer = document.querySelector(containerSelector);
// si no se encuentra el contenedor adecuado, vamos a crearlo
if (!tweetContainer) {
callback(tweet);
return;
} tweetContainer.textContent = tweet;

// la llamada se verá así:


insertTweet("una respuesta al tweet de Elon Musk", ".tweets", handleError);

Este enfoque tiene dos ventajas:


 Toda la funcionalidad que se invoca cuando se produce un error se
almacena en un solo lugar, lo que significa que es fácil de cambiar y añadir
nuevas características.
 Ahora podemos hacer que la función insertTweet() realice diferentes
acciones en diferentes situaciones:
Copiar códigoJAVASCRIPT
/* si el contenedor no se encuentra en una página,
el callback lo creará y colocará un tweet allí */

insertTweet(
"una respuesta al tweet de Elon Musk",
".tweets",
function () {
const newTweetContainer = document.createElement("div");
newTweetContainer.textContent = tweet;
document.body.append(newTweetContainer);
}
);

/* aquí, si no se encuentra el contenedor de tweets, una


función callback registrará un mensaje en la consola */

insertTweet("algún hilo raro", ".tweets", function () {


console.log"¡Oh, vamos! No voy a hacer nada");
});

Configuramos el callback para que funcione con código síncrono. Cada bloque de
código se ejecuta uno tras otro, y la secuencia ya está determinada. Esto es
completamente diferente cuando tu código tiene que esperar a que ocurra algo.
En este caso, nuestro código es asíncrono, y los callbacks son muy útiles en este
caso. Hablaremos de esto en la próxima unidad, después de practicarlo un poco.

Lección 2 Callbacks
Escribe tu propia implementación del método forEach() que tenga dos
parámetros: un array y un callback. La función debe hacer un bucle sobre el array
sin utilizar los métodos forEach(), map() o reduce().
const tweets = [
"algún hilo raro",
"una respuesta al tweet de Elon Musk",
"reacción a las noticias de última hora"
];
function forEach(arr, callback) {
// escribe tu código aquí
for (let i = 0; i < arr.length; i++) {
callback(arr[i], i, arr);
}
}
forEach(tweets, function (tweet) {
console.log(tweet);
});

Callbacks asíncronos
En la unidad anterior, aprendimos sobre los callbacks síncronos. Ahora es el
momento de hablar de los callbacks asíncronos.
Callbacks utilizados para subir imágenes
Consideremos el siguiente ejemplo: necesitamos escribir una función para cargar
imágenes en un sitio web. Para ello, primero crearemos un elemento <img>
llamando al método document.createElement(). A continuación, asignaremos el
atributo src a la imagen para que el navegador sepa desde dónde cargarla:
Copiar códigoJAVASCRIPT

function loadImage(imageUrl) {
// Crea el objeto imagen (objeto de la imagen)
const img = document.createElement("img");
img.src = imageUrl; // Especifica la ruta de acceso a la imagen

return img;
}

// Ahora podemos insertar la imagen en el diseño


const img = loadImage("https://yastatic.net/q/logoaas/v1/Practicum.svg");
document.body.append(img);

El código se ejecutará de la siguiente manera:


 El motor declara una función.
 A continuación, declara la variable img en la penúltima línea del código
anterior.
 Ejecuta el código de la función loadImage().
 Crea un elemento <img> e incluye un enlace a la imagen.
 Mientras se carga la imagen, el motor creará un nodo DOM donde colocar
la imagen.
 Una vez cargada la imagen, el motor la renderizará.
Hay un problema con los dos últimos pasos. Cuando el motor crea un nodo en el
DOM, el marcado se desplaza para hacer hueco a un nuevo elemento. Luego,
cuando la imagen se cargue y aparezca en la página, el marcado se desplazará de
nuevo. Si la imagen no se carga en absoluto, el usuario verá el siguiente cuadro:

Para evitar estas situaciones, necesitamos crear un nodo DOM después de cargar
la imagen. Es en estos casos donde los callbacks resultan útiles.
El objeto de la imagen tiene dos propiedades: onload y onerror, en las que
podemos colocar funciones. La primera función será invocada cuando se cargue la
imagen. La segunda se ejecutará cuando se produzca un error.
Estas funciones deben ser almacenadas en los métodos de los objetos
correspondientes:
Copiar códigoJAVASCRIPT

// El callback debe ser ejecutado después de


// que la imagen se cargue
function imageLoadCallback(evt) {
// Una vez cargada la imagen, añade el objeto de la imagen al DOM
document.body.append(evt.target);
}
// La función para cargar la imagen
function loadImage(imageUrl, loadCallback) {
const img = document.createElement("img");
img.src = imageUrl;

// Este método se utilizará al cargar la imagen


img.onload = loadCallback;
}

// Ahora la imagen aparecerá en el diseño, después de cargarla


loadImage(
"https://yastatic.net/q/logoaas/v1/Practicum.svg",
imageLoadCallback
);

Bien! Ahora el elemento DOM se creará solo después de que se cargue la imagen,
por lo que el diseño no tendrá más fallos.
Este es un ejemplo de cómo trabajar con código asíncrono. Renderizar imágenes
en una página es parte del trabajo de la API del navegador, y la interacción con
esta API es asíncrona. Como puedes ver, tras pedir al servidor que cargue la
imagen, el navegador ha empezado a ejecutar otro trozo de código.
Nuestra tarea consistía en gestionar la interacción con el código asíncrono. En
primer lugar, le indicamos al motor que renderice la imagen solo cuando se
cargue. Para indicarle eso, colocamos la función para renderizar la imagen en la
propiedad onload.
Los callbacks son una forma de trabajar con el código. Sin embargo, este enfoque
tiene varios inconvenientes, que discutiremos en la próxima unidad.

Lección 3 callback Asíncronos


Esta vez, el enlace a una imagen de un cachorro está roto. Tendrás que controlar
esto escribiendo un callback de error.
Al principio del archivo script.js, declara la función de callback handleLoadError().
La función handleLoadError() debería registrar el siguiente mensaje en la consola:
Image not loaded. ERROR! ERROR!
Una vez hecho esto, pasa esta función como tercer argumento de la función
loadImage().

function handleLoadError(){
console.log('Image not loaded. ERROR! ERROR!');
}
function handleImageLoad(evt) {
// Añade el elemento de la imagen al DOM después de cargar la imagen
document.body.append(evt.target);
}
// Completa el código de esta función
function loadImage(imageUrl, loadCallback, errorCallback) {
const img = document.createElement("img");
img.src = imageUrl;
img.onload = loadCallback;
img.onerror = errorCallback;
}
loadImage(
"https://pictures.s3.yandex.net/frontend-developer/functions/dog-12345.jpg",
handleImageLoad,
handleLoadError
);

Temporizadores

En las unidades anteriores, hemos tratado el código asíncrono y cómo trabajar


con él utilizando callbacks. Ha llegado el momento de aprender sobre los
temporizadores.
Los temporizadores son funciones especiales integradas en el navegador, que nos
permiten retrasar la ejecución de alguna pieza de código diciéndole al navegador
que espere algún tiempo antes de ejecutarlo. Por ejemplo, utilizando
temporizadores, podríamos hacer que una ventana de chat emergente apareciera
un cierto tiempo después de que el usuario haya cargado la página, como cuando
una página de noticias atrae a los usuarios con un artículo clickbait antes de
ponerles un muro de pago. Como alternativa, podrías utilizar un temporizador
para algo más útil y hacer que la interfaz del correo electrónico se actualice
automáticamente cada cierto tiempo.
Hay dos métodos para establecer temporizadores: setTimeout() y setInterval().
Ahora hablaremos de cada uno de ellos.
El método setTimeout()

Este método ejecuta un código específico una vez transcurrido un tiempo


determinado. Toma los siguientes argumentos como entrada:
 El primer argumento es una función callback que contiene el código que
debe ejecutar.
 El segundo argumento es la cantidad de tiempo en milisegundos que la
función debe esperar antes de ejecutar el callback.
 Además, el método debe tomar cualquier argumento que deba ser pasado
al callback.
Este es un ejemplo sencillo que muestra un mensaje en la consola 10 segundos
después de que la página se haya cargado:

Copiar códigoJAVASCRIPT

function showMessage(message) {
console.log(message);
}

setTimeout(showMessage, 10000, "Han pasado 10 segundos desde que se cargó la página");

/* 10 segundos (o 10 mil milisegundos) después de la carga de la página,


aparecerá un mensaje en la consola: */

Podemos borrar el temporizador llamando al método clearTimeout(). Para ello,


tenemos que asignar la llamada al método setTimeout() a una variable y pasar esa
variable al método clearTimeout(). Este método se utiliza en las páginas web que
cierran la sesión del usuario cuando ha estado inactivo durante un cierto tiempo,
como las páginas web de banca en línea. Para implementar esta función,
necesitamos establecer un temporizador, que comenzará la cuenta atrás una vez
que se abra la página y se reiniciará cada vez que el usuario interactúe con ella.
Copiar códigoJAVASCRIPT

function logOut() {
// La lógica de cerrar la sesión del usuario después de un cierto período de inactividad
//No te preocupes. Pronto serás capaz de escribir esa lógica tú mismo
}

// Cierra la sesión del usuario en el sistema una vez transcurridos 300 segundos
let timer = setTimeout(logOut, 300000);

// Si el usuario hace clic en algún lugar de la página,


// reinicia el temporizador y espera otra interacción del usuario
window.addEventListener("click", function () {
clearTimeout(timer);
timer = setTimeout(logOut, 300000);
}

Establecer un temporizador de repetición con el método setInterval()

El método setInterval() establece un temporizador de repetición, que permite


ejecutar una función de callback varias veces en un intervalo de tiempo
especificado. Por ejemplo, puede utilizarse para actualizar automáticamente la
bandeja de entrada:
Copiar códigoJAVASCRIPT

function checkEmail() {
// Aquí está el código para comprobar los nuevos correos electrónicos.
// Pronto podrás escribir ese código tú mismo.
}

// La bandeja de entrada se actualizará automáticamente cada 10 segundos


setInterval(checkEmail, 10000);
Cuando el temporizador ya no es necesario, podemos eliminarlo para ahorrar
recursos del navegador. Para ello, tenemos que pasar el temporizador al método
clearInterval():
Copiar códigoJAVASCRIPT

const interval = setInterval(checkEmail, 10000);

// Si el usuario ha cambiado de pestaña,


window.addEventListener("blur", function () {
// el evento blur se dispara cuando un elemento ya no está enfocado
clearInterval(interval); // elimina el temporizador.
}

// Si el usuario ha vuelto,
window.addEventListener("focus", function() {
// el evento focus se dispara cuando el usuario hace clic en el elemento o pulsa la tecla Tab
interval = setInterval(checkEmail, 10000); // inicia el temporizador de nuevo.
}

Sea cual sea el tiempo que establezcas para los métodos setTimeout() o
setInterval(), el retraso o intervalo real siempre será un poco mayor debido a la
cantidad de acciones que el procesador tiene que realizar. Cuando pasas un
callback, se añade a la cola de tareas.

Lección 4 Temporizadores
Hemos escrito una función para registrar la hora actual en la consola. Termina el
código y escríbelo para que la hora actual se registre en la consola cada segundo.

function consoleDate() {
const date = new Date();
console.log(`${date.getHours()}:${date.getMinutes()}:${date.getSeconds()}`);
}

Bucle de eventos
En las unidades anteriores, hemos hablado del comportamiento asíncrono y de las formas en que
podemos trabajar con él utilizando callbacks. A veces, puede ser difícil entender el orden exacto en el que
se ejecutará el código asíncrono. Veamos el siguiente ejemplo:

Copiar códigoJAVASCRIPT

console.log("Este mensaje será el primero que aparezca en la consola");

setTimeout(function () {
console.log("Este mensaje aparecerá en tercer lugar");
}, 1);

console.log("Este mensaje se registrará en segundo lugar");

Hablemos sobre por qué los mensajes se registran en este orden.

En primer lugar, el motor ejecuta la función console.log() en la primera línea de código. Para ello, el
motor coloca console.log() en una cola junto a otras llamadas a funciones que están esperando su
ejecución. Esta cola recibe el nombre de pila de llamadas.

A continuación, el motor pasa a la función setTimeout(). Sin embargo, el motor no puede calcular
realmente el tiempo; esto lo hace el navegador. Así que, el motor se remite a la API del navegador y le
pide que establezca un temporizador de 1 ms.

Por lo tanto, la petición ha sido enviada a la API, y el motor procede a su siguiente tarea, que en este caso
es añadir la siguiente llamada a console.log() a la pila de llamadas; es decir, la que está en la línea inferior
de código.

Después de eso, ha transcurrido 1 milisegundo, tal y como habíamos especificado en el método


setTimeout(), por lo que el motor recibe una señal de la API para ejecutar el callback. Por lo tanto, esta
función se añade a la cola de tareas. Si hay otras tareas en la pila de llamadas, el motor las ejecutará
primero. Entonces, cuando la pila de llamadas esté despejada, el método console.log() dentro de la
función setTimeout() será finalmente empujado a la pila de llamadas y ejecutado.

¿Cómo funciona la pila de llamadas?


Todos los comandos que programamos se colocan en la pila de llamadas, que, en términos simples, es
como una lista de espera para nuestro código. Examinemos este proceso en detalle:

Copiar códigoJAVASCRIPT

console.log("Eeny, meeny, miny, moe");


console.log("Catch a tiger by the toe");

 La tarea para registrar el string "Eeny, meeny, miny, moe" se introduce en la pila de llamadas.
 El motor ejecuta la tarea y registra el string en la consola.
 La siguiente tarea se introduce en la pila de llamadas: registra el string "Catch a tiger by the toe".
 El motor ejecuta esta tarea y registra otro string en la consola.
Pero también se pueden emplear tareas mucho más complejas que produzcan otras tareas:

Copiar códigoJAVASCRIPT
function workHardPlayHard() {
function work() {
console.log("Trabaja duro");
}

function play() {
console.log("Juega duro");
}

work();
play();
}

workHardPlayHard();

En el ejemplo anterior, el proceso es más complejo:

 La tarea para ejecutar la función workHardPlayHard() se introduce en la pila de llamadas.


 El motor comienza a ejecutar el cuerpo de la función workHardPlayHard(). La función
workHardPlayHard() permanecerá en la pila de llamadas hasta que se haya ejecutado todo lo
que hay dentro del cuerpo de la función.
 La tarea para ejecutar la función work() aparece en la pila de llamadas. Esta llamada a la función
será enviada sobre la función workHardPlayHard().
 El motor comienza a ejecutar la función work() y envía la tarea para registrar el string "Work
hard" a la pila de llamadas.
 Ahora, no hay más tareas en la función work(), por lo que el motor puede ejecutarla. El string
"Work hard" se registra en la consola, y la llamada a console.log() y la llamada a work()
desaparecen de la pila de llamadas.
 La función play() es enviada a la pila de llamadas, luego la llamada a console.log() con el
argumento "Play hard" es enviada a la pila de llamadas encima de la llamada anterior.
 La pila de llamadas se borra de nuevo. La llamada a console.log() desaparece de la pila de
llamadas, seguida de play(), y finalmente workHardPlayHard(), dejando la pila de llamadas vacía.
Así es como la pila de llamadas cambiará paso a paso

Podemos pensar en la pila de llamadas como un conjunto de objetos físicos apilados unos sobre otros.
Imagina una baraja de cartas, por ejemplo. Antes de poder sacar la carta que está en el fondo del mazo,
primero hay que eliminar todas las que están encima.

Así es como funciona la pila de llamadas cuando tenemos funciones anidadas. Echa otro vistazo a la
imagen de arriba. La tarjeta workHardPlayHard() es la primera que se añade a la pila, seguida de la
tarjeta work() y, por último, la tarjeta console.log(). En este punto, la tarjeta console.log() está en la parte
superior de la pila. Si queremos eliminar cualquier otra carta de la pila, tenemos que empezar por ésta.

Por lo tanto, siempre que tengamos funciones anidadas, el motor trabajará hacia adentro cuando las
añada a la pila de llamadas, y luego trabajará hacia afuera cuando las borre.

Cola de callbacks y bucle de eventos


Este es el ejemplo del principio de la unidad:

Copiar códigoJAVASCRIPT

console.log("Este mensaje será el primero que aparezca en la consola");

setTimeout(function () {
console.log("Este mensaje aparecerá en tercer lugar");
}, 1);

console.log("Este mensaje se registrará en segundo lugar");

Vamos a darle un poco de sabor:

Copiar códigoJAVASCRIPT

/* Advertencia: si ejecutas este código en la consola,


la pestaña de tu navegador se congelará durante varios minutos.
No lo hagas si
no quieres esperar a que el código deje de ejecutarse. */

console.log("Este mensaje será el primero que aparezca en la consola");

setTimeout(function () {
console.log("¿Cuándo crees que se registrará este mensaje?");
}, 1);

for (let i = 0; i <= 1000000; i += 1) {


console.log("Este mensaje se registrará un millón uno de veces");
}

¿Cuándo aparecerá el mensaje "¿Cuándo crees que se registrará este mensaje?"?

Si no estás familiarizado con el bucle de eventos (Event Loop), puedes pensar en lo siguiente: como se
tarda más de 1ms en mostrar un string en la consola un millón de veces, el mensaje "¿Cuándo crees que
se registrará este mensaje?" se registrará en algún lugar entre la secuencia de registros "Este mensaje se
registrará un millón uno de veces".

Sin embargo, eso es incorrecto. Veamos cómo el motor JS procesará realmente estas llamadas. Enviará
las tareas a la pila de llamadas en el siguiente orden:

 El string "Este mensaje se registrará primero en la consola" se registra en la consola.


 Se envía una solicitud a la API del navegador para establecer el temporizador en 1 ms.
 El string "Este mensaje se registrará un millón de veces" se registra en la consola 1.000.000 de
veces.

Después de eso, el motor recibe una señal de la API del navegador diciendo que el temporizador se ha
apagado. Ahora tiene que ejecutar el código de callback, es decir, registrar el string "¿Cuándo crees que se
registrará este mensaje?" en la consola. Entonces, el motor envía la tarea a la pila de llamadas.
 El string "¿Cuándo crees que se registrará este mensaje?" se registra en la consola.
Así, cuando se produce un evento, la tarea se añade a la cola de tareas. A continuación, el bucle de
eventos recoge las tareas de la cola y las pasa a la pila de llamadas para ejecutarlas.
Pregunta

¿En qué orden se mostrarán los mensajes en la consola?

Copiar códigoJAVASCRIPT

console.log(1);

// pon el temporizador a 0 milisegundos


setTimeout(function () {
console.log(2);
}, 0);
console.log(3);

1, 2, 3

2, 1, 3

1, 3, 2

Comprobar respuesta

Promises
Hace un tiempo, cuando todo el mundo seguía utilizando jQuery, el código para
ejecutar callbacks en secuencias podía ser similar a lo que mostramos a
continuación (no esperamos que entiendas cada línea de esta monstruosidad):
Copiar códigoJAVASCRIPT

// una función que muestra cuántos amigos comunes tienen Tristan y Kristen

$.ajax({
url: "https://socialnetwork.com/users",
success: function (data) {
// encuentra el usuario necesario
const tristan = data.users.find(user => user.name === "Tristan");

// enviar solicitud de amistad a Tristán


$.ajax({
url: `https://socialnetwork.com/users/${tristan.id}/friends`,
success: function (data) {
const tristanFriends = data.friends;
const kristen = tristanFriends.find(friend => friend.name === "Kristen");

// enviar solicitud de amistad a Kristen


$.ajax({
url: `https://socialnetwork.com/users/${kristen.id}/friends`,
success: function (data) {
const kristenFriends = data.friends;
let mutualFriends = 0;

for (let i = 0; i < tristanFriends.length; i += 1) {


const tristanFriendId = tristanFriends[i].id;
const isMutual = kristenFriends.some(friend => friend.id === tristanFriendId);

if (isMutual) {
mutualFriends += 1;
}
}

alert(`Kristen y Tristan tienen ${mutualFriends} amigos mutuos`););


}
});
}
});
}
});

Para ello, tenemos que invocar un callback desde un callback dentro de un


callback, y este es un ejemplo relativamente sencillo.
Una "escalera" de callbacks como ésta se conoce como "callback hell" o "pyramid
of doom". En cierto punto, tales construcciones se vuelven simplemente
imposibles de leer, por lo que los callbacks no se utilizan para la interacción
compleja entre cliente y servidor. Afortunadamente, hay una solución. Podemos
hacer uso de una función llamada promises.

Este vídeo te ayudará a entender el concepto de esta función en JavaScript


https://youtu.be/WB2zFk2J5Gk
Un promise es un objeto que representa el cumplimiento o el fracaso de una
operación asíncrona. Ofrece a los desarrolladores algo a lo que referirse y con lo
que trabajar mientras esperan a que se complete el código asíncrono. Vamos a
negociar nuestra salida del callback hell utilizando promises.
Copiar códigoJAVASCRIPT

ajax("https://socialnetwork.com/users")
.then(getTristan)
.then(getKristen)
.then(getMutualFriends)
.then((mutualFriends) => {
alert(`Kristen y Tristan tienen ${mutualFriends} amigos mutuos`);
});

Es una gran mejora, ¿no lo crees? El nuevo código es más fácil y mucho más
agradable de leer. Ahora, vamos a desglosar la forma en la que realmente
funciona.
El concepto de promises
En algunas cafeterías y restaurantes de comida rápida, después de hacer un
pedido, recibes un localizador, un dispositivo especial que empieza a vibrar y a
sonar cuando tu pedido está listo. De este modo, sabrás cuándo puedes ir a
recogerlo. Los localizadores son una analogía bastante exacta del concepto
promise.
Cuando haces un pedido, recoges el localizador y te sientas en tu mesa. Después,
puedes dedicarte a tus asuntos: navegar por Twitter, hablar por teléfono o leer un
libro. Cuando el localizador empieza a sonar, dejas lo que estás haciendo y vas a
por tu pedido.
Los promises son como los localizadores de JavaScript. Nos permiten describir un
código que no debe ejecutarse inmediatamente, sino solo después de que se
produzca un evento determinado.
Sintaxis
Los promises se utilizan a menudo para enviar peticiones a los dispositivos, como
los servidores. Una solicitud puede ser resolved (resuelta) o rejected (rechazada).
Por lo tanto, al crear un promise, tenemos que describir las instrucciones para dos
casos: cuando la solicitud se resuelve, y cuando se rechaza. Pero antes de
programar esta lógica, veamos cómo el motor determina si nuestra petición ha
sido procesada o no.
Para "enseñar" al motor a procesar las peticiones, pasamos una función al
constructor Promise(). Esta función de callback, llamada ejecutor, toma dos
callbacks como argumentos: resolve() y reject(). Estos callbacks cambian el estado
de promise a "resolved" o "rejected" desde su estado inicial de "pending"
(pendiente).
Copiar códigoJAVASCRIPT

const newPromise = new Promise(function (resolve, reject) {


// Aprenderás a enviar peticiones al servidor muy pronto. Por ahora,
// determinaremos aleatoriamente si la solicitud ha sido procesada o no
const rand = Math.random() > 0.5;

if (rand) {
resolve("Solicitud procesada satisfactoriamente");
} else {
reject("Solicitud rechazada");
}
});

// puedes usar los nombres que quieras, pero


// comunmente se suele utilizar "resolve" y "reject"

El código de la función que se pasa al constructor Promise() se ejecuta de forma


automática e inmediata. En cuanto abras el archivo, el motor ejecutará el código
de promise. En el ejemplo anterior, el motor creará la variable rand y le asignará
un valor de true o false. Además, dependiendo del valor de la variable rand, el
motor cambiará el estado del promise a "resolved" o "rejected".
A continuación, tenemos que programar la lógica, es decir, lo que el motor debe
hacer en caso de que el promise se resuelva, o si es rechazado. Para ello,
JavaScript nos proporciona tres métodos:
 then() —se ejecuta si promise se ha resuelto
 catch() — se ejecuta si promise ha sido rechazado
 finally() — se ejecuta en cualquier caso
Los dos primeros métodos - then() y catch() - normalmente con un parámetro.
Estos parámetros son los valores que pasamos a resolve() y reject() al crear el
promise. En nuestro ejemplo, el valor es "Request processed successfully" o
"Request rejected" dependiendo del estado del promise:
Copiar códigoJAVASCRIPT

// crear un promise
const newPromise = new Promise(function(resolve, reject) {
// determina aleatoriamente si la solicitud ha sido procesada o no
const rand = Math.random() > 0.5 ? true : false;

if (rand) {
resolve("Solicitud procesada satisfactoriamente");
} else {
reject("Solicitud rechazada");
}
});

newPromise
.then(function (value) { // se ejecuta si promise se ha resuelto

/* el parámetro value almacena el valor pasado al


método resolve() al crear el promise, es decir
el string "Solicitud procesada satisfactoriamente" */

console.log(value);
})
.catch(function (value) { // se ejecuta si promise ha sido rechazado
/* en este caso, el parámetro value almacena el valor
pasado al método reject(), es decir
el string "Solicitud rechazada" */

console.log(value + ". Sentimos las molestias.");


})
.finally(function () { // se ejecuta en ambos casos
console.log("Prometemos que recibimos tu solicitud");
});

En resumen, los promises se crean con Promise(), a la que se le pasa un callback.


Este callback toma dos funciones que pueden ser invocadas en el cuerpo del
callback.
La primera función que se pasa al callback es resolve(). Cambia el estado del
promise a "resolved" (resuelto). El valor pasado a esta función será pasado al
método then().
La segunda función es reject(). Cambia el estado del promise a "rejected"
(rechazado). El valor pasado a esta función se pasará al método catch().
En resumen, la sintaxis para crear un promise es la siguiente: primero llama al
constructor Promise(), y luego describe la lógica para procesar que se resuelva o
que se rechace en los métodos then() y catch(), respectivamente.
Encadenar promises
Supongamos que has creado un promise y has descrito tu lógica en los métodos
then() y catch(). Pero, ¿qué pasa si necesitamos más de una petición del servidor?
Por ejemplo, digamos que solicitas una lista de publicaciones de un determinado
usuario. Los procesas y obtienes la publicación que buscabas, y luego quieres
acceder a sus comentarios. Para ello, tendrás que hacer otra petición al servidor,
esta vez para acceder a la lista de comentarios.
Los promises permiten añadir tareas a una cola asíncrona escribiendo otra
llamada then() o catch() en el código. En otras palabras, puedes encadenar
múltiples métodos then() y catch(). El primer método de la cadena tomará como
entrada los valores pasados a sus respectivas funciones resolve() y reject(),
mientras que todos los siguientes recibirán el resultado devuelto por las llamadas
anteriores a then() y catch().
Fíjate en el siguiente ejemplo con las funciones firstAction(), secondAction() y
thirdAction():
Copiar códigoJAVASCRIPT

const newPromise = new Promise(function (resolve, reject) {


resolve("Un Mississippi"); // obtiene inmediatamente el promise resuelto
});

function firstAction(value) {
/* el parámetro value recibirá lo que le pasamos
al método resolve() al crear el promise,
es decir, el string "Un Mississippi" */

return `${value}, dos Mississippis`;


}

function secondAction(value) {
/* el valor será igual al valor retornado
por el método then() anterior, es decir, el string "Un Mississippi, dos Mississippis" */

return `${value}, tres Mississippis`;


}

function thirdAction(value) {
console.log(value);
}

newPromise.then(firstAction).then(secondAction).then(thirdAction);

/* veremos lo siguiente en la consola: "Un Mississippi, dos Mississippis, tres Mississippis" */

La cadena de llamadas then() se escribe a menudo como una columna indentada,


como mostramos a continuación:
Copiar códigoJAVASCRIPT

newPromise
.then(firstAction)
.then(secondAction)
.then(thirdAction);

Intentemos aplicar el código anterior a los promises rechazados:


Copiar códigoJAVASCRIPT
const newPromise = new Promise(function (resolve, reject) {
reject("Un Mississippi"); // hace que el promise sea rechazado al instante
});

function firstAction(value) {
return `${value}, dos Mississippis`;
}

function secondAction(value) {
return `${value}, tres Mississippis`;
}

function thirdAction(value) {
console.log(value);
}

newPromise
.then(firstAction)
.then(secondAction)
.catch(thirdAction);

/* la consola registra: "Un Mississippi" --- porque newPromise fue rechazado,


y obtuvimos inmediatamente la llamada catch() */

Ten en cuenta que el código de la función, que pasamos a los métodos catch() y
then(), también puede dar lugar a un error.
Termina siempre una cadena de promises con una llamada catch(). Esto
controlará el error si ocurre en cualquiera de las llamadas a then() en la cadena.
Métodos estáticos de promise
Promise() tiene métodos integrados. Resultan muy útiles a la hora de crear
promises. Ten en cuenta que los promises no tienen estos métodos; sólo la
función Promise() los tiene.
Promise.resolve() y Promise.reject()
No es necesario llamar a new Promise() cuando quieras crear inmediatamente un
promise resuelto o rechazado. Simplemente, puedes utilizar los métodos
Promise.resolve() y Promise.reject(). Estos métodos crean un promise, cambian su
estado a "resolved" o "rejected", respectivamente, y almacenan el valor que se
les pasa como resultado de este promise.
Copiar códigoJAVASCRIPT

Promise.resolve("Este promise ha sido resuelto")


.then(function (value) {
console.log(value); // "Este promise ha sido resuelto"
});

Promise.reject("Este promise ha sido rechazado")


.catch(function (value) {
console.log(value); // "Este promise ha sido rechazado"
});

Promise.all()
Puedes tener varios promises en tu página. Por ejemplo, solicitas una imagen de
un servidor y un texto escrito por otra persona para crear una entrada de un blog
compuesta por ambos. Este post solo sirve para su propósito si contiene tanto el
texto como la imagen, y uno no tiene sentido sin el otro. En otras palabras,
queremos crear la publicación después de que se resuelvan ambos promises.
Hay un método estático llamado Promise.all() que podemos utilizar para esto.
Toma un array de entrada con promises y ejecuta el código escrito en then() solo
cuando el estado de cada promise está "resolved":
Copiar códigoJAVASCRIPT

// crea el primer promise


const firstPromise = new Promise((resolve, reject) => {
if (someCondition) {
resolve("Primer promise");
} else {
reject();
}
});

// crea el segundo promise


const secondPromise = new Promise((resolve, reject) => {
if (secondCondition) {
resolve("Segundo promise");
} else {
reject();
}
});

// crea un array de promises


const promises = [firstPromise, secondPromise]

// pasa el array de promises al método Promise.all()


Promise.all(promises)
.then((results) => {
console.log(results); // ["Primer promise", "Segundo promise"]
});

Como tal, los promises son peticiones al código asíncrono. Cuando creamos un
promise, lo que le pedimos al motor es: ejecuta este código y, en función de los
resultados, cambia el estado del promise a "resolved" o "rejected".
Todas las acciones posteriores con el resultado de la solicitud se escriben en la
cadena de métodos then() y catch(). Toman como argumento un callback con un
parámetro. Este parámetro recibe el valor que devolvió la función then() o catch()
anterior, o el valor con el que se llamó a la función resolve() o reject().

También podría gustarte