# TEMA 9: Programación asíncrona y buenas prácticas ## 1. Programación síncrona/asíncrona JavaScript es _single-threaded_ y en principio síncrono. También puede tener comportamiento asíncrono (para por ejemplo consultar APIs). Soluciones asíncronas: callback, promises, async/await... - **Comunicación síncrona (o bloqueante)**: Cuando hay comunicación síncrona entre dos entes A y B, si A envía un mensaje a B, deja de actuar hasta que B responde. - **Comunicación asíncrona (o no bloqueante)**: Cuando hay comunicación asíncrona entre dos entes A y B, si A envía un mensaje a B, puede seguir actuando y B responderá cuando pueda. ### - Funciones callback Una función callback es una función que se pasa como parámetro. ![[Pasted image 20240312153525.png|500]] Generalmente se usan para definir el código asociado a un evento (por ejemplo un botón cuando se haga click en él). ![[Pasted image 20240312153619.png|450]] ### - Promises Antes se usaba un objeto tipo Promise para gestionar llamadas asíncronas. La promesa llamaba a una función callback ("`then`") cuando la operación asíncrona finalizaba con éxito, y a otra diferente ("`catch`") si fallaba. Como no es muy intuitivo, se introdujo en 2017 `async/await`. ### - Async/await ![[Pasted image 20240312154046.png]] En un comportamiento síncrono, hasta que la API no responda no carga la página completa y se queda bloqueada "cargando". Con **async/await**, se puede solucionar esto convirtiendo las funciones en asíncronas. Las funciones marcadas con **async** se ejecutarán de forma paralela al hilo principal. ![[Pasted image 20240312154300.png|500]] Usando **await** se espera a la respuesta de la API, y se convierte en bloqueante, pero `loadPhotos()` sigue siendo asíncrona. ![[Pasted image 20240312154500.png|500]] ## 2. Buenas prácticas ### - Consideraciones generales - Usar siempre `;` tras cada instrucción. - Usar `let` para declarar variables en vez de `var`; - Usar notación camelCase. - Usar `{}` en lugar de `new Object()` y `[]` en lugar de `new Array()`. - Usar siempre el mismo tipo de comillas, salvo casos excepcionales. - Usar siempre la misma unidad de identación (4 espacios/1 tab). - No confundir `for...of` para iterar arrays con `for...in` para iterar claves. - Usar el comparador de igualdad `===` en lugar de `==` (y `!==` en vez de `!=`). ### - Modo estricto - Usar siempre el modo estricto (`"use strict";`): - Impide usar variables no declaradas. - Impide que se pueda cambiar valores de elementos NaN o Infinity. - Impide definir un Object con valores repetidos. - Convierte en errores cosas que generalmente serían warnings. ### - Puntos de entrada - Usar una función de entrada `main()`, que deberá ser **async** si tiene llamadas a API u operaciones asíncronas. ![[Pasted image 20240312155414.png|500]] ### - Módulos Los módulos permiten importar y exportar funciones y variables de otros módulos. Se recomienda su uso ya que, si no, todo lo que sea definido globalmente en un archivo JS será accesible **desde cualquier otro archivo**. Sólo es necesario enlazar un script (B) en el HTML ya que las sentencias import/export se encargan de cargar automáticamente el resto de archivos (A u otros) JS referenciados. ### - Organización del código ![[Pasted image 20240312155900.png|150]] - La carpeta **js** contendrá todo el código JS incluidas las vistas (controladores para archivos html, ej. index.html -> index.js). - La carpeta **api** contiene los módulos que se encargan de la comunicación con los endpoints de la API. - La carpeta **libs** contiene librerías JS externas que usemos. - La carpeta **renderers** contiene módulos para transformar objetos JS en entidades HTML. - La carpeta **utils** contiene módulos con funciones de utilidad (como `include()`, o `parseHTML()`). - La carpeta **validators** contiene módulos cuya función es validar datos recibidos en forms. # TEMA 10: Manipulación del DOM y renders ## 1. Modelo DOM (Document Object Model) Modelo que se crea al cargar una página en el navegador. Contiene una jerarquía de elementos en forma de árbol. Con JS se puede: - Cambiar los elementos HTML de la página - Cambiar los valores de los atributos de dichos elementos - Cambiar los estilos CSS asociados a los elementos Eliminar elementos y atributos existentes en el HTML - Añadir nuevos elementos y atributos HTML - Ejecutar funciones y scripts en respuesta a los eventos originados en los elementos HTML - Crear nuevos manejadores de eventos - Eliminar manejadores de eventos En el DOM cada objeto es un nodo (_node_). Cada nodo tiene padre, excepto el raíz. Cada nodo puede tener $n$ hijos. Si dos nodos tienen el mismo padre son hermanos. ![[Pasted image 20240319154406.png|500]] ## 2. Manipulación del DOM DOM ofrece una API con métodos y propiedades para manipular elementos HTML. El primer paso es encontrar el elemento y a partir de ahí modificar. Algunos métodos son: - `node.getElementById(id)`: Obtiene un elemento a partir de su ID. - `node.getElementsByTagName(tagName)`: Obtiene un array de descendientes del nodo que sean de un tipo de etiqueta. - `node.getElementsByClassName(className)`: Obtiene un array de descendientes del nodo con una clase determinada. - `node.querySelectorAll(cssSelector)`: Obtiene un array de descendientes del nodo que concuerdan con un selector CSS. - `node.classList`: Devuelve la lista de clases del nodo (mutable). - `node.tagName`: Devuelve el tipo de etiqueta HTML del nodo. - `node.style`: Proporciona acceso para modificar y consultar los estilos. - `node.innerHTML`: Devuelve y permite modificar el contenido HTML de un nodo. - `node.innerText`: Devuelve y permite modificar el texto de un nodo. - `node.getAttribute("atributo")`: Permite consultar el valor de un atributo del nodo. - `node.setAttribute(nombreAtributo, valor)`: Modifica el valor de un atributo. - `document.createElement(nombreElemento)`: Crea un nuevo elemento HTML. - `node.removeChild(elemento)`: Elimina un nodo hijo. - `node.remove()`: Elimina el nodo. - `node.append(elemento)`: Añade un elemento como **último** hijo. - `node.prepend(elemento)`: Añade un elemento como **primer** hijo. - `node.insertBefore(elemento, hermano)`: Añade un elemento como nodo hijo, por delante de un nodo hermano. - `node.replaceChild(nuevo, antiguo)`: Reemplaza un nodo hijo con otro. - `node.parentNode`: Devuelve el nodo padre. - `node.firstChild`: Devuelve el primer hijo del nodo. - `node.lastChild`: Devuelve el último hijo del nodo. - `node.childNodes[i]`: Devuelve el i-ésimo hijo del array de hijos del nodo. - `node.nextSibling`: Devuelve el siguiente hermano de un nodo. - `node.previousSibling`: Devuelve el hermano anterior de un nodo. El DOM tiene varias propiedades extra como: - `document.URL` - `document.body` - `document.cookie` - `document.doctype` - `document.head` - `document.inputEncoding` - `document.readyState` - `document.title` ### - NodeLists Hay propiedades para acceder directamente a colecciones de objetos: - `document.images`: Todas las imágenes del documento. - `document.forms`: Todos los formularios del documento. - `document.forms[X].elements`: Todos los campos del formulario X. - `document.links`: Todos los `` del documento. - `document.scripts`: Todos los scripts. ## 3. Renderizadores Normalmente se necesita mostrar muchos elementos del mismo tipo, como imágenes o mensajes de éxito/error. O mostrar el mismo elemento de más de una forma diferente (miniatura o completa). # TEMA 12: Llamadas AJAX mediante REST y JSON ## 1. REST Arquitectura cliente-servidor. No tiene estado ya que se basa en HTTP. Cacheable. Modelo en capas. ![[Pasted image 20240409154435.png]] ## 2. Formatos de comunicación JSON ![[Pasted image 20240409154700.png]] ### - Tipos de datos - **Number:** números decimales con signo, incluyendo notación exponencial. - **Array:** elementos separados por "," y entre "[ ]". Pueden ser de cualquier tipo. - **String:** secuencia de caracteres entre "". Los caracteres especiales se escapan con "\\". - **Object:** colección no ordenada de pares K,V entre "{ }" y separados por ",". - **null:** valores vacíos. Undefined en JS. - **Boolean:** true/false. ## 3. Implementación de llamadas asíncronas REST ### - AJAX (Asynchronous JavaScript And XML) Inicialmente se usaba XML pero ahora JSON. El núcleo de AJAX es la API XMLHttpRequest o XHR. Algunas implementaciones de AJAX son XHR, fetch (nativas), jQuery, axios (externas). ### - Fetch API nativa basada en Promises. La respuesta es una Promise. Cuando se resuelve la Promise se puede consultar el objeto Response. ### - jQuery La librería incluye diversos métodos para AJAX. Basada en XHR. Facilita el código y es cross-browser pero es externa, y se usa FormData. ### - Axios Basada en XHR pero usa funciones asíncronas. Como ventajas: - Intercepta request y response (permite examinar y modificar las peticiones y respuestas HTTP). - Permite cancelar requests. - Múltiples librerías de terceros como "addons". - Compatibilidad con muchos navegadores. - Simplifica la lógica de gestión de respuestas y errores. - Muy popular actualmente (incluso con Node.js). La única desventaja es que es externa. #### NOTA: Al capturar errores hay que capturar `error.response.data.message` en el catch y no solo el objeto error. ## 4. Módulos API - `/js/api/common.js`: URL Base y configuración común para todas las peticiones. - `/js/api/OBJETO.js`: módulo de acceso a API para un OBJETO (ej. Fotos). # TEMA 13: Persistencia en cliente ## 1. HTTP Cookies Una cookie HTTP (o web cookies, Internet cookies, browser cookies) es un pequeño fragmento de información enviado desde un sitio web y que el navegador almacena en local mientras este navega por el sitio. ![[Pasted image 20240423154342.png|500]] - **Cookies de sesión (in-memory, transient, non-persistent)**: Son volátiles y se almacenan en memoria y se borran al cerrar el navegador. No suelen tener fecha de expiración. - **Cookies persistentes (tracking)**: Se almacenan incluso con el navegador cerrado. Tienen fecha de expiración. Hay distintos tipos según su origen: - **First-party cookies:** Las envía el sitio web que el usuario visita. - **Third-party cookie:** Las envía un sitio web externo al visitado (likes, adSense, iframes). Hay varios metadatos, como Domain, Path, Same-site, Secure, HTTPOnly, Max-Age, Size. Las cookies tienen varias finalidades como gestionar sesiones (productos añadidos al carrito, puntuaciones de juegos, auth cookies), tracking (páginas visitadas, datos de formularios, estadísticas) o preferencias del usuario (lenguaje, colores, divisa). ### - Problemas de seguridad Las cookies forman parte de las request, por lo que hay que validarlas y contemplar la posibilidad de que formen parte de un ataque. **Nunca** se debe almacenar información sensible. Las third party cookies se pueden usar para tracking o para ataques XSRF. Sin embargo pueden ser útiles para integrar componentes de un sitio en otro (google maps, youtube, etc). ## 2. Web Storage API Permite al navegador almacenar pares clave/valor de forma más intuitiva. Más seguro que las cookies. Permite mayor tamaño de almacenamiento. ### - SessionStorage Mantiene un área de almacenamiento separada para cada origen mientras el navegador esté abierto. Cuando se cierra se borran los datos. El límite es 5MB. Accesible mediante `Window.sessionStorage`. ### - LocalStorage Mantiene un área de almacenamiento separada para cada origen incluso al cerrar el navegador. Almacena datos sin fecha de expiración o y sólo se borra mediante JS o borrando el caché del navegador. Límite 5MB. Accesible mediante `Window.localStorage`. ## 3. Cache API