From 43b6978e184fa03ffb24f432bef7c9f568638ce3 Mon Sep 17 00:00:00 2001 From: Brais Moure Date: Thu, 20 Feb 2025 14:19:31 +0100 Subject: [PATCH 01/10] Clase 4 Intermedio | 19/02/2025 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Programación asíncrona --- Intermediate/07-async.js | 154 +++++++++++++++++++++++++++++ Intermediate/08-async-exercises.js | 50 ++++++++++ README.md | 9 +- 3 files changed, 211 insertions(+), 2 deletions(-) create mode 100644 Intermediate/07-async.js create mode 100644 Intermediate/08-async-exercises.js diff --git a/Intermediate/07-async.js b/Intermediate/07-async.js new file mode 100644 index 00000000..26a2008b --- /dev/null +++ b/Intermediate/07-async.js @@ -0,0 +1,154 @@ +/* +Clase 4 - Programación asíncrona (19/02/2025) +Vídeo: https://www.twitch.tv/videos/2385650388?t=00h22m48s +*/ + +// Programación asíncrona + +// Código síncrono + +console.log("Inicio") + +for (let i = 0; i < 100000000; i++) { } + +console.log("Fin") + +// Event Loop (Bucle de eventos) + +// Componentes del Event Loop: +// 1. Call Stack (Pila de ejecución) +// 2. Web APIs (APIs del navegador) o Node.js: +// 3. Task Queue (setTimeout()) y Microtask Queue (Promesas) + +// Flujo del Event Loop: +// 1. Call Stack +// 2. Operaciones asíncronas -> Web APIs o Node.js +// 3. Operación termina -> La coloca en Task Queue o Microtask Queue +// 4. Si Call Stack vacío -> Mueve tareas del Microtask Queue o Task Queue al Call Stack +// 5. El proceso se repite + +// Código asíncrono + +// - Callbacks + +console.log("Inicio") + +setTimeout(() => { + console.log("Esto se ejecuta después de 2 segundos") +}, 2000) + +console.log("Fin") + +// - Problema: Callback Hell + +function step1(callback) { + setTimeout(() => { + console.log("Paso 1 completado") + callback() + }, 1000) +} + +function step2(callback) { + setTimeout(() => { + console.log("Paso 2 completado") + callback() + }, 1000) +} + +function step3(callback) { + setTimeout(() => { + console.log("Paso 3 completado") + callback() + }, 1000) +} + +step1(() => { + step2(() => { + step3(() => { + console.log("Todos los pasos completados") + }) + }) +}) + +// - Promesas + +const promise = new Promise((resolve, reject) => { + // IMPORTANTE: Inicialmente escribí setInterval, pero lo correcto es setTimeout + // setInterval se ejecutaría indefinidamente cada 4s, y el proceso nunca finalizaría + setTimeout(() => { + const ok = false + if (ok) { + resolve("Operación exitosa") + } else { + reject("Se ha producido un error") + } + }, 4000) +}) + +promise + .then(result => { + console.log(result) + }) + .catch(error => { + console.log(error) + }) + +// - Encadenamiento de promesas + +function step1Promise() { + return new Promise(resolve => { + setTimeout(() => { + console.log("Paso 1 con promesa completado") + resolve() + }, 1000) + }) +} + +function step2Promise() { + return new Promise(resolve => { + setTimeout(() => { + console.log("Paso 2 con promesa completado") + resolve() + }, 1000) + }) +} + +function step3Promise() { + return new Promise(resolve => { + setTimeout(() => { + console.log("Paso 3 con promesa completado") + resolve() + }, 1000) + }) +} + +step1Promise() + .then(step2Promise) + .then(step3Promise) + .then(() => { + console.log("Todos los pasos con promesa completados") + }) + +// - Async/Await + +function wait(ms) { + return new Promise(resolve => setTimeout(resolve, ms)) +} + +async function process() { + + console.log("Inicio del proceso") + + await wait(5000) + console.log("Proceso después de 5 segundos") + + await wait(1000) + console.log("Proceso después de 1 segundo") + + await wait(2000) + console.log("Proceso después de 2 segundos") + + console.log("Fin del proceso") +} + +process() \ No newline at end of file diff --git a/Intermediate/08-async-exercises.js b/Intermediate/08-async-exercises.js new file mode 100644 index 00000000..c7904f9a --- /dev/null +++ b/Intermediate/08-async-exercises.js @@ -0,0 +1,50 @@ +/* +Clase 4 - Programación asíncrona (19/02/2025) +Vídeo: https://www.twitch.tv/videos/2385650388?t=00h22m48s +*/ + +// 1. Crea una función para saludar que reciba un nombre y un callback. +// El callback debe ejecutarse después de 2 segundos y mostrar en consola "Hola, [nombre]". + +// 2. Crea tres funciones task1(callback), task2(callback) y task3(callback). +// Cada función debe tardar 1 segundo en ejecutarse y luego llamar al callback. + +// 3. Crea una función para verificar un número que retorne una Promesa. +// Si el número es par, la promesa se resuelve con el mensaje "Número par". +// Si el número es impar, la promesa se rechaza con el mensaje "Número impar". + +// 4. Crea tres funciones que devuelvan promesas: +// firstTask(): tarda 1s y muestra "Primera tarea completada". +// secondTask(): tarda 2s y muestra "Segunda tarea completada". +// thirdTask(): tarda 1.5s y muestra "Tercera tarea completada". + +// 5. Transforma el ejercicio anterior de Promesas en una función async/await llamada executeTasks(). + +// 6. Crea una función getUser(id) que devuelva una promesa y simule una llamada a una API (que se demore 2s). +// Si el id es menor a 5, la promesa se resuelve con { id, nombre: "Usuario " + id }. +// Si el id es 5 o mayor, la promesa se rechaza con el mensaje "Usuario no encontrado". +// Usa async/await para llamar a getUser(id) y maneja los errores con try/catch. + +// 7. Intenta predecir el resultado de este código antes de ejecutarlo en la consola: +// console.log("Inicio") +// setTimeout(() => console.log("setTimeout ejecutado"), 0) +// Promise.resolve().then(() => console.log("Promesa resuelta")) +// console.log("Fin") + +// 8. Crea tres funciones que devuelvan promesas con tiempos de espera distintos. +// A continuación, usa Promise.all() para ejecutarlas todas al mismo tiempo y mostrar "Todas las promesas resueltas" cuando terminen. + +// 9. Crea una función waitSeconds(segundos) que use setTimeout dentro de una Promesa para esperar la cantidad de segundos indicada. +// A continuación, usa async/await para que se espere 3 segundos antes de mostrar "Tiempo finalizado" en consola. + +// 10. Crea una simulación de un cajero automático usando asincronía. +// - La función checkBalance() tarda 1s y devuelve un saldo de 500$. +// - La función withdrawMoney(amount) tarda 2s y retira dinero si hay suficiente saldo, o devuelve un error si no hay fondos. +// - Usa async/await para hacer que el usuario intente retirar 300$ y luego 300$ más. +// +// Posible salida esperada: +// Saldo disponible: 500$ +// Retirando 300$... +// Operación exitosa, saldo restante: 200$ +// Retirando 300$... +// Error: Fondos insuficientes \ No newline at end of file diff --git a/README.md b/README.md index c40eb2f1..3d0fa36b 100644 --- a/README.md +++ b/README.md @@ -15,8 +15,8 @@ ### Curso de fundamentos intermedio (continuación del desde cero). Nueva clase cada semana. -#### 🔴 PRÓXIMA CLASE EN DIRECTO: Miércoles 19 de febrero a las 20:00h (España) en [Twitch](https://twitch.tv/mouredev) y [YouTube](https://youtube.com/@mouredev) -#### 🗓️ CONSULTA EL HORARIO POR PAÍS Y CREA UN RECORDATORIO desde [Discord](https://discord.gg/umXned72?event=1339346133624946889) +#### 🔴 PRÓXIMA CLASE EN DIRECTO: Miércoles 26 de febrero a las 20:00h (España) en [Twitch](https://twitch.tv/mouredev) y [YouTube](https://youtube.com/@mouredev) +#### 🗓️ CONSULTA EL HORARIO POR PAÍS Y CREA UN RECORDATORIO desde [Discord](https://discord.gg/DuE3fHyV?event=1341887890858442772) * Clase 1 [29/01/2025] - Funciones avanzadas * [Vídeo](https://www.twitch.tv/videos/2367024319?t=00h08m45s) @@ -34,6 +34,11 @@ * [Código Clases](./Intermediate/05-advanced-classes.js) * [Ejericios](./Intermediate/06-advanced-objects-classes-exercises) +* Clase 4 [19/02/2025] - Programación asíncrona + * [Vídeo](https://www.twitch.tv/videos/2385650388?t=00h22m48s) + * [Código](./Intermediate/07-async.js) + * [Ejericios](./Intermediate/08-async-exercises.js) + ## Clases en vídeo ### Curso de fundamentos desde cero From 40fc52f00c42b624d4acd6072c8f62240bbf590c Mon Sep 17 00:00:00 2001 From: Brais Moure Date: Thu, 27 Feb 2025 06:56:10 +0100 Subject: [PATCH 02/10] Clase 5 Intermedio | 26/02/2025 Manejo de APIs --- Intermediate/09-apis.js | 171 ++++++++++++++++++++++++++++++ Intermediate/10-apis-exercises.js | 24 +++++ README.md | 11 +- 3 files changed, 204 insertions(+), 2 deletions(-) create mode 100644 Intermediate/09-apis.js create mode 100644 Intermediate/10-apis-exercises.js diff --git a/Intermediate/09-apis.js b/Intermediate/09-apis.js new file mode 100644 index 00000000..c6cec658 --- /dev/null +++ b/Intermediate/09-apis.js @@ -0,0 +1,171 @@ +/* +Clase 5 - Manejo de APIs (26/02/2025) +Vídeo: https://www.twitch.tv/videos/2391820998?t=00h17m25s +*/ + +// Manejo de APIs + +// - APIs REST (HTTP + URLs + JSON) + +// Métodos HTTP: +// - GET +// - POST +// - PUT +// - DELETE + +// Códigos de respuesta HTTP: +// - 200 OK +// - 201 +// - 400 +// - 404 +// - 500 + +// Consumir una API + +// https://jsonplaceholder.typicode.com + +// GET +fetch("https://jsonplaceholder.typicode.com/posts") + .then(response => { + // Transforma la respuesta a JSON + return response.json() + }) + .then(data => { + // Procesa los datos + console.log(data) + }) + .catch(error => { + // Captura errores + console.log("Error", error) + }) + +// Uso de Async/Await + +async function getPosts() { + try { + const response = await fetch("https://jsonplaceholder.typicode.com/posts") + const data = await response.json() + console.log(data) + } catch (error) { + console.log("Error", error) + } +} + +getPosts() + +// Solicitud POST + +async function createPost() { + try { + + const newPost = { + userId: 1, + title: "Este es el título de mi post", + body: "Este es el cuerpo de mi post" + } + + const response = await fetch("https://jsonplaceholder.typicode.com/posts", { + method: "POST", + headers: { + "Content-Type": "application/json" + }, + body: JSON.stringify(newPost) + }) + + const data = await response.json() + console.log(data) + } catch (error) { + console.log("Error", error) + } +} + +createPost() + +// Herramientas para realizar peticiones HTTP +// - https://postman.com +// - https://apidog.com +// - https://thunderclient.com + +// Manejo de errores + +fetch("https://jsonplaceholder.typicode.com/mouredev") + .then(response => { + if (!response.ok) { + throw Error(`Status HTTP: ${response.status}`) + } + return response.json() + }) + .catch(error => { + console.log("Error", error) + }) + +// Métodos HTTP adicionales +// - PATCH +// - OPTIONS + +async function partialPostUpdate() { + try { + const response = await fetch("https://jsonplaceholder.typicode.com/posts/10", { + method: "PATCH", + headers: { + "Content-Type": "application/json" + }, + body: JSON.stringify({ title: "Este es el nuevo título de mi post" }) + }) + + const data = await response.json() + console.log(data) + } catch (error) { + console.log("Error", error) + } +} + +partialPostUpdate() + +// Autenticación mediante API Key + +async function getWeather(city) { + + // https://openweathermap.org + const apiKey = "TU_API_KEY" + const url = `https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${apiKey}` + + try { + const response = await fetch(url) + const data = await response.json() + console.log(data) + } catch (error) { + console.log("Error", error) + } +} + +getWeather("Madrid") + +// Otros métodos de Autenticación y Autorización +// - Bearer Tokens +// - JWT + +// Versionado de APIs +// - https://api.example.com/v1/resources +// - https://api.example.com/v2/resources + +// Otras APIs + +async function getPokemon(pokemon) { + + // https://pokeapi.co + const url = `https://pokeapi.co/api/v2/pokemon/${pokemon}` + + try { + const response = await fetch(url) + const data = await response.json() + console.log(`Habilidades de ${data.name}`) + data.abilities.forEach(ability => { + console.log(ability.ability.name) + }) + } catch (error) { + console.log("Error", error) + } +} + +getPokemon("pikachu") \ No newline at end of file diff --git a/Intermediate/10-apis-exercises.js b/Intermediate/10-apis-exercises.js new file mode 100644 index 00000000..d1452bc3 --- /dev/null +++ b/Intermediate/10-apis-exercises.js @@ -0,0 +1,24 @@ +/* +Clase 5 - Manejo de APIs (26/02/2025) +Vídeo: https://www.twitch.tv/videos/2391820998?t=00h17m25s +*/ + +// 1. Realiza una petición GET con fetch() a JSONPlaceholder y muestra en la consola la lista de publicaciones + +// 2. Modifica el ejercicio anterior para que verifique si la respuesta es correcta usando response.ok. Si no lo es, lanza y muestra un error + +// 3. Reescribe el ejercicio 1 usando la sintaxis async/await en lugar de promesas + +// 4. Realiza una petición POST a JSONPlaceholder para crear una nueva publicación. Envía un objeto con propiedades como title o body + +// 5. Utiliza el método PUT para actualizar completamente un recurso (por ejemplo, modificar una publicación) en JSONPlaceholder + +// 6. Realiza una petición PATCH para modificar únicamente uno o dos campos de un recurso existente + +// 7. Envía una solicitud DELETE a la API para borrar un recurso (por ejemplo, una publicación) y verifica la respuesta + +// 8. Crea una función que realice una solicitud GET (la que quieras) a OpenWeatherMap + +// 9. Utiliza la PokéAPI para obtener los datos de un Pokémon concreto, a continuación los detalles de la especie y, finalmente, la cadena evolutiva a partir de la especie + +// 10. Utiliza una herramienta como Postman o Thunder Client para probar diferentes endpoint de una API diff --git a/README.md b/README.md index 3d0fa36b..3706d4fa 100644 --- a/README.md +++ b/README.md @@ -15,8 +15,8 @@ ### Curso de fundamentos intermedio (continuación del desde cero). Nueva clase cada semana. -#### 🔴 PRÓXIMA CLASE EN DIRECTO: Miércoles 26 de febrero a las 20:00h (España) en [Twitch](https://twitch.tv/mouredev) y [YouTube](https://youtube.com/@mouredev) -#### 🗓️ CONSULTA EL HORARIO POR PAÍS Y CREA UN RECORDATORIO desde [Discord](https://discord.gg/DuE3fHyV?event=1341887890858442772) +#### 🔴 PRÓXIMA CLASE EN DIRECTO: Jueves 6 de marzo a las 20:00h (España) en [Twitch](https://twitch.tv/mouredev) y [YouTube](https://youtube.com/@mouredev) +#### 🗓️ CONSULTA EL HORARIO POR PAÍS Y CREA UN RECORDATORIO desde [Discord](https://discord.gg/63Q2Ts6p?event=1344414401603833886) * Clase 1 [29/01/2025] - Funciones avanzadas * [Vídeo](https://www.twitch.tv/videos/2367024319?t=00h08m45s) @@ -39,6 +39,11 @@ * [Código](./Intermediate/07-async.js) * [Ejericios](./Intermediate/08-async-exercises.js) +* Clase 5 [26/02/2025] - Manejo de APIs + * [Vídeo](https://www.twitch.tv/videos/2391820998?t=00h17m25s) + * [Código](./Intermediate/09-apis.js) + * [Ejericios](./Intermediate/10-apis-exercises.js) + ## Clases en vídeo ### Curso de fundamentos desde cero @@ -107,6 +112,8 @@ * Exploradores: [Chrome](https://www.google.com/intl/es_es/chrome/) | [Brave](https://brave.com/download/) * [Visual Studio Code](https://code.visualstudio.com/) * [Guía de estilo](https://google.github.io/styleguide/jsguide.html) +* Clientes HTTP: [Postman](https://postman.com) | [Apidog](https://apidog.com) | [Thunder Client](https://thunderclient.com) +* APIs: [JSONPlaceholder](https://jsonplaceholder.typicode.com) | [OpenWeather](https://openweathermap.org) | [PokéAPI](https://pokeapi.co) ## Únete al campus de programación de la comunidad From 409007e101d7751bbe5655fd28d9ac80d59fa5fe Mon Sep 17 00:00:00 2001 From: Brais Moure Date: Fri, 7 Mar 2025 09:09:52 +0100 Subject: [PATCH 03/10] Clase 6 Intermedio | 06/03/2025 Manejo del DOM --- Intermediate/11-dom.js | 123 +++++++++++++++++++++++++++++++ Intermediate/12-dom-example.html | 23 ++++++ Intermediate/13-dom-example.js | 11 +++ Intermediate/14-tasklist.html | 18 +++++ Intermediate/15-tasklist.js | 32 ++++++++ Intermediate/16-dom-exercises.js | 24 ++++++ README.md | 11 ++- 7 files changed, 240 insertions(+), 2 deletions(-) create mode 100644 Intermediate/11-dom.js create mode 100644 Intermediate/12-dom-example.html create mode 100644 Intermediate/13-dom-example.js create mode 100644 Intermediate/14-tasklist.html create mode 100644 Intermediate/15-tasklist.js create mode 100644 Intermediate/16-dom-exercises.js diff --git a/Intermediate/11-dom.js b/Intermediate/11-dom.js new file mode 100644 index 00000000..a668a51e --- /dev/null +++ b/Intermediate/11-dom.js @@ -0,0 +1,123 @@ +/* +Clase 6 - Manejo del DOM (06/03/2025) +Vídeo: https://www.twitch.tv/videos/2398786900?t=00h11m52s +*/ + +// Manejo del DOM (Document Object Model) + +console.log(document) + +// - Selección de elementos + +// Métodos básicos (selector HTML) + +const myElementById = document.getElementById("id") + +const myElementsByClass = document.getElementsByClassName("class") + +const myElementsByTag = document.getElementsByTagName("tag") + +// Métodos más modernos (selector CSS) + +document.querySelector(".paragraph") +document.querySelectorAll(".paragraph") + +// - Manipulación de elementos + +const title = document.getElementById("title") +title.textContent = "Hola JavaScript" + +const container = document.querySelector(".container") +container.innerHTML = "

Esto es un nuevo párrafo

" + +// - Modificación de atributos + +// Obtención del atributo +const link = document.querySelector("a") +const url = link.getAttribute("href") + +// Establecimiento del atributo +link.setAttribute("href", "https://example.com") + +// Comprobación de atributo +const hasTarget = link.hasAttribute("target") + +// Eliminación de atributos +link.removeAttribute("target") + +// - Interacción con clases CSS + +const box = document.querySelector(".box") +box.classList.add("selected") +box.classList.remove("selected") +box.classList.toggle("selected") + +const button = document.querySelector("button") +button.style.backgroundColor = "blue" +button.style.color = "white" +button.style.padding = "10px" + +// - Creación y eliminación de elementos + +// Creación + +const newParagraph = document.createElement("p") +newParagraph.textContent = "Este es un nuevo párrafo creado desde JS" +newParagraph.style.padding = "8px" + +container.appendChild(newParagraph) + +const itemsList = document.querySelector("ul") +const newItem = document.createElement("li") +newItem.textContent = "Nuevo elemento" + +// Inserción en un lugar concreto + +const secondItem = itemsList.children[1] +itemsList.insertBefore(newItem, secondItem) + +itemsList.append(newItem) +itemsList.prepend(newItem) +secondItem.before(newItem) +secondItem.after(newItem) + +// Eliminación + +newParagraph.remove() + +// Eliminación tradicional + +const parent = newParagraph.parentElement +parent.removeChild(newParagraph) + +// - Elementos del DOM + +function showMsg() { + alert("Clic!") +} + +const sendButton = document.querySelector("#send") +sendButton.addEventListener("click", showMsg) + +sendButton.addEventListener("click", () => { + alert("Clic con una arrow function!") +}) + +// Eventos comunes + +document.addEventListener("DOMContentLoader", () => { + console.log("El DOM está completamente cargado") +}) + +sendButton.addEventListener("mouseenter", () => { + sendButton.style.backgroundColor = "green" +}) + +sendButton.addEventListener("mouseleave", () => { + sendButton.style.backgroundColor = "blue" +}) + +const form = document.querySelector("form") +form.addEventListener("submit", (event) => { + // Código +}) \ No newline at end of file diff --git a/Intermediate/12-dom-example.html b/Intermediate/12-dom-example.html new file mode 100644 index 00000000..471e1042 --- /dev/null +++ b/Intermediate/12-dom-example.html @@ -0,0 +1,23 @@ + + + + + + HTML de ejemplo + + +

Mi título

+ + + + + \ No newline at end of file diff --git a/Intermediate/13-dom-example.js b/Intermediate/13-dom-example.js new file mode 100644 index 00000000..a325bf8d --- /dev/null +++ b/Intermediate/13-dom-example.js @@ -0,0 +1,11 @@ +/* +Clase 6 - Manejo del DOM (06/03/2025) +Vídeo: https://www.twitch.tv/videos/2398786900?t=00h11m52s +*/ + +console.log(document) + +const myH1 = document.querySelector("h1") +console.log(myH1) + +myH1.textContent = "Mi nuevo título" \ No newline at end of file diff --git a/Intermediate/14-tasklist.html b/Intermediate/14-tasklist.html new file mode 100644 index 00000000..186c58d9 --- /dev/null +++ b/Intermediate/14-tasklist.html @@ -0,0 +1,18 @@ + + + + + + Lista de tareas + + +

Mis tareas

+ + +
    + + + \ No newline at end of file diff --git a/Intermediate/15-tasklist.js b/Intermediate/15-tasklist.js new file mode 100644 index 00000000..85e3c014 --- /dev/null +++ b/Intermediate/15-tasklist.js @@ -0,0 +1,32 @@ +/* +Clase 6 - Manejo del DOM (06/03/2025) +Vídeo: https://www.twitch.tv/videos/2398786900?t=00h11m52s +*/ + +const text = document.getElementById("text") +const button = document.getElementById("button") +const list = document.getElementById("list") + +function addTask() { + + if (text.value === "") return + + const newElement = document.createElement("li") + newElement.textContent = text.value + + newElement.addEventListener("click", () => { + newElement.remove() + }) + + list.appendChild(newElement) + + text.value = "" +} + +button.addEventListener("click", addTask) + +text.addEventListener("keypress", (event) => { + if (event.key === "Enter") { + addTask() + } +}) \ No newline at end of file diff --git a/Intermediate/16-dom-exercises.js b/Intermediate/16-dom-exercises.js new file mode 100644 index 00000000..b6a9cd7f --- /dev/null +++ b/Intermediate/16-dom-exercises.js @@ -0,0 +1,24 @@ +/* +Clase 6 - Manejo del DOM (06/03/2025) +Vídeo: https://www.twitch.tv/videos/2398786900?t=00h11m52s +*/ + +// 1. Crea un elemento (por ejemplo, un

    ) y cambia su contenido a "¡Hola Mundo!"" al cargar la página + +// 2. Inserta una imagen con id="myImage" y cambia su atributo src a otra URL + +// 3. Crea un
    sin clases y agrega la clase resaltado cuando se cargue la página + +// 4. Crea un párrafo con id="paragraph" y cambia su color de texto a azul + +// 5. Agrega un botón que, al hacer clic, cree un nuevo elemento
  • con el texto "Nuevo elemento y lo agregue a una lista