diff --git a/files/es/learn/javascript/asynchronous/index.html b/files/es/learn/javascript/asynchronous/index.html deleted file mode 100644 index ddde4c60f170d6..00000000000000 --- a/files/es/learn/javascript/asynchronous/index.html +++ /dev/null @@ -1,43 +0,0 @@ ---- -title: JavaScript asíncrono -slug: Learn/JavaScript/Asynchronous -translation_of: Learn/JavaScript/Asynchronous ---- -
{{LearnSidebar}}
- -

En este módulo echamos un vistazo a {{Glossary("JavaScript")}} {{Glossary("asíncrono")}}, por qué es importante y cómo se puede utilizar para manejar eficazmente las posibles operaciones de bloqueo, como recuperar recursos desde un servidor

- -

Prerrequisitos

- -

JavaScript asíncrono es un tema bastante avanzado, y se recomienda trabajar con los primeros pasos de JavaScript y los módulos de bloques de construcción de JavaScript antes de intentarlo.

- -

Si no está familiarizado con el concepto de programación asincrónica, definitivamente debe comenzar con el artículo Conceptos generales de programación asincrónica en este módulo. Si es así, probablemente pueda pasar al módulo Introducción a JavaScript asíncrono.

- -
-

Nota: Si está trabajando en una computadora / tableta / otro dispositivo donde no tiene la capacidad de crear sus propios archivos, puede probar (la mayoría de) los ejemplos de código en un programa de codificación en línea como JSBin o Glitch

-
- -

Guias

- -
-
Conceptos generales de programación asincrónica
-
-

En este artículo revisaremos una serie de conceptos importantes relacionados con la programación asincrónica y cómo se ve esto en los navegadores web y JavaScript. Debe comprender estos conceptos antes de trabajar en los otros artículos del módulo.

-
-
Introduciendo JavaScript asincrónico
-
En este artículo recapitulamos brevemente los problemas asociados con JavaScript síncrono, y echamos un primer vistazo a algunas de las diferentes técnicas de JavaScript asíncrono que encontrarás, mostrando cómo pueden ayudarnos a resolver tales problemas.
-
JavaScript asíncrono cooperativo: tiempos de espera e intervalos
-
Aquí observamos los métodos tradicionales que JavaScript tiene disponibles para ejecutar código de forma asíncrona después de que haya transcurrido un período de tiempo establecido, o en un intervalo regular (por ejemplo, un número establecido de veces por segundo), hablemos sobre para qué son útiles y observe su Problemas inherentes.
-
Manejo de operaciones asincrónas con Promises
-
Las promesas son una característica relativamente nueva de JavaScript que le permite diferir más acciones hasta que la acción anterior se haya completado o responder en caso de tener una falla o error durante su ejecución. Esto es realmente útil para configurar una secuencia de operaciones para que funcione correctamente. Este artículo te muestra cómo funcionan las promesas, dónde las verá en uso en una WebAPI y cómo escribir las tuyas de la manera adecuada.
-
Hacer la programación asincróna más fácil con async y await
-
Las promesas pueden ser algo complejas de configurar y comprender, por lo que los navegadores modernos han implementado funciones async y el operador de await. El primero permite que las funciones estándar se comporten implícitamente de forma asíncrona con las promesas, mientras que el segundo puede usarse dentro de las funciones async para esperar las promesas antes de que la función continúe. Esto hace que las promesas de encadenamiento sean más simples y fáciles de leer.
-
Elegir el enfoque correcto
-
Para finalizar este módulo, consideraremos las diferentes técnicas y características de codificación que hemos discutido a lo largo de todo, y veremos cuáles deberias usar cuando, con recomendaciones y recordatorios de dificultades comunes, sea lo más apropiado.
-
- -

Ver también

- - diff --git a/files/es/learn/javascript/asynchronous/index.md b/files/es/learn/javascript/asynchronous/index.md new file mode 100644 index 00000000000000..91b187e61ce98f --- /dev/null +++ b/files/es/learn/javascript/asynchronous/index.md @@ -0,0 +1,35 @@ +--- +title: JavaScript asíncrono +slug: Learn/JavaScript/Asynchronous +translation_of: Learn/JavaScript/Asynchronous +--- +{{LearnSidebar}} + +En este módulo echamos un vistazo a {{Glossary("JavaScript")}} {{Glossary("asíncrono")}}, por qué es importante y cómo se puede utilizar para manejar eficazmente las posibles operaciones de bloqueo, como recuperar recursos desde un servidor + +## Prerrequisitos + +JavaScript asíncrono es un tema bastante avanzado, y se recomienda trabajar con los primeros pasos de JavaScript y los módulos de [bloques de construcción de JavaScript](/es/docs/Learn/JavaScript/Building_blocks) antes de intentarlo. + +Si no está familiarizado con el concepto de programación asincrónica, definitivamente debe comenzar con el artículo [Conceptos generales de programación asincrónica](/es/docs/Learn/JavaScript/Asynchronous/Concepts) en este módulo. Si es así, probablemente pueda pasar al [módulo Introducción a JavaScript asíncrono](/es/docs/Learn/JavaScript/Asynchronous/Introducing). + +> **Nota:** Si está trabajando en una computadora / tableta / otro dispositivo donde no tiene la capacidad de crear sus propios archivos, puede probar (la mayoría de) los ejemplos de código en un programa de codificación en línea como [JSBin](http://jsbin.com/) o [Glitch](https://glitch.com) + +## Guias + +- [Conceptos generales de programación asincrónica](/es/docs/Learn/JavaScript/Asynchronous/Concepts) + - : En este artículo revisaremos una serie de conceptos importantes relacionados con la programación asincrónica y cómo se ve esto en los navegadores web y JavaScript. Debe comprender estos conceptos antes de trabajar en los otros artículos del módulo. +- [Introduciendo JavaScript asincrónico](/es/docs/Learn/JavaScript/Asynchronous/Introducing) + - : En este artículo recapitulamos brevemente los problemas asociados con JavaScript síncrono, y echamos un primer vistazo a algunas de las diferentes técnicas de JavaScript asíncrono que encontrarás, mostrando cómo pueden ayudarnos a resolver tales problemas. +- [JavaScript asíncrono cooperativo: tiempos de espera e intervalos](/es/docs/Learn/JavaScript/Asynchronous/Loops_and_intervals) + - : Aquí observamos los métodos tradicionales que JavaScript tiene disponibles para ejecutar código de forma asíncrona después de que haya transcurrido un período de tiempo establecido, o en un intervalo regular (por ejemplo, un número establecido de veces por segundo), hablemos sobre para qué son útiles y observe su Problemas inherentes. +- [Manejo de operaciones asincrónas con Promises](/es/docs/Learn/JavaScript/Asynchronous/Promises) + - : Las promesas son una característica relativamente nueva de JavaScript que le permite diferir más acciones hasta que la acción anterior se haya completado o responder en caso de tener una falla o error durante su ejecución. Esto es realmente útil para configurar una secuencia de operaciones para que funcione correctamente. Este artículo te muestra cómo funcionan las promesas, dónde las verá en uso en una WebAPI y cómo escribir las tuyas de la manera adecuada. +- [Hacer la programación asincróna más fácil con async y await](/es/docs/Learn/JavaScript/Asynchronous/Async_await) + - para esperar las promesas antes de que la función continúe. Esto hace que las promesas de encadenamiento sean más simples y fáciles de leer. +- [Elegir el enfoque correcto](/es/docs/Learn/JavaScript/Asynchronous/Choosing_the_right_approach) + - : Para finalizar este módulo, consideraremos las diferentes técnicas y características de codificación que hemos discutido a lo largo de todo, y veremos cuáles deberias usar cuando, con recomendaciones y recordatorios de dificultades comunes, sea lo más apropiado. + +## Ver también + +- [Asynchronous Programming](https://eloquentjavascript.net/11_async.html) from the fantastic [Eloquent JavaScript](https://eloquentjavascript.net/) online book by Marijn Haverbeke. diff --git a/files/es/learn/javascript/asynchronous/introducing/index.html b/files/es/learn/javascript/asynchronous/introducing/index.html deleted file mode 100644 index 0c56f5ea06c063..00000000000000 --- a/files/es/learn/javascript/asynchronous/introducing/index.html +++ /dev/null @@ -1,163 +0,0 @@ ---- -title: General asynchronous programming concepts -slug: Learn/JavaScript/Asynchronous/Introducing -tags: - - Aprender - - Hilos - - JavaScript - - Promesas - - Threads - - bloques -translation_of: Learn/JavaScript/Asynchronous/Concepts -original_slug: Learn/JavaScript/Asynchronous/Concepts ---- -
{{LearnSidebar}}{{NextMenu("Learn/JavaScript/Asynchronous/Introducing", "Learn/JavaScript/Asynchronous")}}
- -

En este artículo, repasaremos una serie de conceptos importantes relacionados con la programación asincrónica y cómo se ve esto en los navegadores web y JavaScript. Debe comprender estos conceptos antes de trabajar con los demás artículos del módulo.

- - - - - - - - - - - - -
Pre-requisitos:Literatura básica de computadora, un razonable entendimiento de los fundamentos de JavaScript.
Objetivo:Entender los conceptos básicos detrás de la programación asincrónica, y cómo se manifiesta en los exploradores web y JavaScript.
- -

¿Asincrónico?

- -

Normalmente, el código de un programa determinado se ejecuta directamente, y solo sucede una cosa a la vez. Si una función se basa en el resultado de otra función, tiene que esperar a que la otra función termine y regrese, y hasta que eso suceda, todo el programa se detiene esencialmente desde la perspectiva del usuario.

- -

Los usuarios de Mac, por ejemplo, a veces experimentan esto como un cursor giratorio multicolor (o "beachball" - "bola de playa" - como es llamado frecuentemente). Este cursor es la manera que tiene el sistema operativo de decir "el actual programa que está usando tiene que parar y esperar que algo termine, y está tomando tanto tiempo que me preocupa que pienses qué está sucediendo."

- -

Multi-colored macOS beachball busy spinner

- -

Esto es una experiencia frustrante y no es un buen uso del poder de procesamiento de una computadora - especialmente en una era donde las computadoras tienen múltiples procesadores disponibles. No tiene sentido sentarse allí a esperar algo cuando podrías dejar que la otra tarea se ejecute en otro procesador y le notifique cuando termine. Mientras tanto, esto le permitiría terminar otros trabajos, lo cual es la base de la programación asincrónica. Depende del entorno de programación que esté usando (exploradores web, en caso de desarrollo web) proveer de APIs que le permitan ejecutar dichas tareas de manera asincrónica.

- -

Código de bloqueo (Blocking)

- -

Las técnicas asincrónicas son muy útiles, particularmente en programación web. Cuando una app web se ejecuta en el navegador y ejecuta un gran bloque de código sin retornar el control al navegador, este mismo puede parecer que se congela. Esto es llamado blocking; el navegador es bloqueado para que el usuario pueda seguir interactuando y realizando otras tareas hasta que la app web retorne el control sobre el procesador.

- -

Vamos a ver algunos ejemplos que muestren lo que significa blocking.

- -

En nuestro ejemplo simple-sync.html (véalo en vivo), agregamos un detector del evento click ("click event listener") a un botón con el fin de que cuando sea clickeado, ejecute una operación de un gran consumo de tiempo (calcula 10 millones de fechas y luego muestra la última en la consola) y luego agrega un párrafo al DOM:

- -
const btn = document.querySelector('button');
-btn.addEventListener('click', () => {
-  let myDate;
-  for(let i = 0; i < 10000000; i++) {
-    let date = new Date();
-    myDate = date
-  }
-
-  console.log(myDate);
-
-  let pElem = document.createElement('p');
-  pElem.textContent = 'This is a newly-added paragraph.';
-  document.body.appendChild(pElem);
-});
- -

Cuando ejecute el ejemplo, abra su consola de JavaScript y haga click en el botón — notará que el párrafo no aparece hasta que las fechas hayan sido calculadas en su totalidad y el mensaje en la consola haya sido logueado. EL código se ejecuta en el orden en que aparece (de arriba hacia abajo), y la última operación no se ejecuta hasta que la anterior haya terminado.

- -
-

Nota: El ejemplo anterior es poco realista. ¡Nunca se van a calcular 10 millones de fechas en una app web real! Sin embargo, sirve para dar una idea básica.

-
- -

En nuestro segundo ejemplo, simple-sync-ui-blocking.html (véalo en vivo), se simula algo un poco más realista con el que se puede encontrar en una página real. Se bloquea la interacción del usuario con la carga ("rendering") de la UI. En este ejemplo, se tienen dos botones:

- - - -
function expensiveOperation() {
-  for(let i = 0; i < 1000000; i++) {
-    ctx.fillStyle = 'rgba(0,0,255, 0.2)';
-    ctx.beginPath();
-    ctx.arc(random(0, canvas.width), random(0, canvas.height), 10, degToRad(0), degToRad(360), false);
-    ctx.fill()
-  }
-}
-
-fillBtn.addEventListener('click', expensiveOperation);
-
-alertBtn.addEventListener('click', () =>
-  alert('You clicked me!')
-);
- -

Si se clickea el primer botón y rápidamente se clickea el segundo, se verá que la alerta no aparece hasta que los círculos hayan terminado de representarse. La primer operación blockea a la segunda hasta que esta haya terminado de ejecutarse.

- -
-

Nota: OK, nuestro caso es feo y estamos fingiendo el efecto de bloqueo, pero es un problema común con el que los desarrolladores de aplicaciones reales batallan todo el tiempo.

-
- -

¿Por qué es esto? La respuesta es porque JavaScript, en general, es de "un solo hilo" (single-threaded). En este punto, se tiene que introduce el concepto de "hilos" (threads).

- -

Threads

- -

Un hilo (thread) es básicamente un proceso simple que un programa puede usar para completar tareas ("tasks"). Cada hilo solo puede realizar una tarea a la vez:

- -
Task A --> Task B --> Task C
- -

Cada tarea se va a ejecutar secuencialmente; una tarea tiene que completarse antes de que la próxima empiece.

- -

Como se dijo previamente, muchas computadores actualmente tienen múltiples procesadores, por lo que pueden realizar múltiples tareas a la vez. Los lenguajes de programación que pueden manejar múltiples hilos pueden usar múltiples procesadores para completar múltiples tareas en simultáneo.

- -
Thread 1: Task A --> Task B
-Thread 2: Task C --> Task D
- -

JavaScript es single-threaded

- -

JavScript es tradicionalmente single-threaded. Aún con múltiples procesadores, solo se puede ejecutar tareas en un solo hilo, llamado el hilo principal (main thread). El ejemplo de arriba se ejecuta de la siguiente manera:

- -
Main thread: Render circles to canvas --> Display alert()
- -

Después de un tiempo, JavaScript ganó algunas herramientas que ayudaron con dichos problemas. Web workers permiten que se envíe parte del procesamiento de JavaScript a un hilo separado, llamado worker con el fin de que puedan ejecutar múltiples pedazos de JavaScript en simultáneo. Generalmente se usará un worker para ejectuar procesos de mucho consumo del hilo principal (main thread) con el fin de que no se bloquee la interacción del usuario.

- -
  Main thread: Task A --> Task C
-Worker thread: Expensive task B
- -

Con esto en mente, miremos el ejemplo simple-sync-worker.html (véalo ejecutándose en vivo) nuevamente con la consola de JavaScript del navegador abierta. Esto es una re-escritura del ejemplo anterior que calculaba 10 millones de fechas en hilos worker separados. Ahora si se clickea el botón, el navegador tiene permitido mostrar el párrafo antes de que las fechas haya terminado de calcularse. La primer operación ya no bloquea a la segunda.

- -

Código asincrónico

- -

Los web workers son muy útiles, pero tienen limitaciones. La mayor es que no pueden acceder al {{Glossary("DOM")}} — no se puede logar que un worker modifique directamente algo de la UI. No se puede representar 1 millón de círculos azules en un worker; básicamente solo puede hacer el cálculo numérico.

- -

El segundo problema es que a pesar de que el código se ejecuta en un worker no es bloqueador, es simplemente sincrónico. Esto se convierte en un problema cuando una función depender en los resultados de múltiples procesos previos para funcionar. Considere el siguiente diagrama de hilos:

- -
Main thread: Task A --> Task B
- -

En este caso, digamos que la Tarea A (Task A) está haciendo algo como buscando una imagen de un servidor y la Tarea B (Task B) luego hace algo con la imagen, como aplicarle un filtro. Si se ejecuta la Tarea A y luego inmediatamente se trata de ejecutar la Tarea B, se obtendrá un error, porque la imagen todavía no estará disponible.

- -
  Main thread: Task A --> Task B --> |Task D|
-Worker thread: Task C -----------> |      |
- -

En este caso, digamos que la Tarea D hace uso de los resultados de la Tarea B y la Tarea C. Se se puede garantizar que esos resultados estarán disponibles al mismo tiempo, entonces tal vez estemos OK, pero es poco probable. Si la Tarea D trata de ejecutarse cuando uno de sus inputs no está disponible, disparará un error.

- -

Para arreglar dichos problemas, los navegadores nos permiten ejecutar ciertas operaciones asincrónicamente. Características como las Promises (Promesas) permiten establecer la ejecución de una operación (por ejemplo, buscar una imagen desde un servidor), y luego esperar hasta que el resultado sea retornado antes de ejecutar otra operación.

- -
Main thread: Task A                   Task B
-    Promise:      |__async operation__|
- -

Como la operación está sucediendo en otro lugar, el hilo principal no está bloqueado mientras la operación asincrónica está siendo procesada.

- -

Vamos a empezar a ver cómo se puede escribir código asincrónico en el próximo artículo. Cosas emocionantes, ¿eh? ¡Siga leyendo!

- -

Conclusión

- -

El diseño del software moderno gira cada más entorno a la programación asincrónica, para permiterle a los programas hacer más de una cosa a la vez. A medida que use nuevas y más poderosas APIs, encontrará más casos donde la única forma de realizar las cosas es asincrónicamente. Era muy difícil escribir el código asincrónico. Todavía lleva tiempo acostumbrarse, pero se ha vuelto mucho más sencillo. En el resto de este módulo, exploraremos porqué el código asincrónico importa y cómo diseñar código que evite algunos de los problemas que hemos descrito en este artículo.

- -

En este módulo

- - diff --git a/files/es/learn/javascript/asynchronous/introducing/index.md b/files/es/learn/javascript/asynchronous/introducing/index.md new file mode 100644 index 00000000000000..89302c1bcdfee4 --- /dev/null +++ b/files/es/learn/javascript/asynchronous/introducing/index.md @@ -0,0 +1,165 @@ +--- +title: General asynchronous programming concepts +slug: Learn/JavaScript/Asynchronous/Introducing +tags: + - Aprender + - Hilos + - JavaScript + - Promesas + - Threads + - bloques +translation_of: Learn/JavaScript/Asynchronous/Concepts +original_slug: Learn/JavaScript/Asynchronous/Concepts +--- +{{LearnSidebar}}{{NextMenu("Learn/JavaScript/Asynchronous/Introducing", "Learn/JavaScript/Asynchronous")}} + +En este artículo, repasaremos una serie de conceptos importantes relacionados con la programación asincrónica y cómo se ve esto en los navegadores web y JavaScript. Debe comprender estos conceptos antes de trabajar con los demás artículos del módulo. + + + + + + + + + + + + +
Pre-requisitos: + Literatura básica de computadora, un razonable entendimiento de los + fundamentos de JavaScript. +
Objetivo: + Entender los conceptos básicos detrás de la programación asincrónica, y + cómo se manifiesta en los exploradores web y JavaScript. +
+ +## ¿Asincrónico? + +Normalmente, el código de un programa determinado se ejecuta directamente, y solo sucede una cosa a la vez. Si una función se basa en el resultado de otra función, tiene que esperar a que la otra función termine y regrese, y hasta que eso suceda, todo el programa se detiene esencialmente desde la perspectiva del usuario. + +Los usuarios de Mac, por ejemplo, a veces experimentan esto como un cursor giratorio multicolor (o "beachball" - "bola de playa" - como es llamado frecuentemente). Este cursor es la manera que tiene el sistema operativo de decir "el actual programa que está usando tiene que parar y esperar que algo termine, y está tomando tanto tiempo que me preocupa que pienses qué está sucediendo." + +![Multi-colored macOS beachball busy spinner](https://mdn.mozillademos.org/files/16577/beachball.jpg) + +Esto es una experiencia frustrante y no es un buen uso del poder de procesamiento de una computadora - especialmente en una era donde las computadoras tienen múltiples procesadores disponibles. No tiene sentido sentarse allí a esperar algo cuando podrías dejar que la otra tarea se ejecute en otro procesador y le notifique cuando termine. Mientras tanto, esto le permitiría terminar otros trabajos, lo cual es la base de la **programación asincrónica**. Depende del entorno de programación que esté usando (exploradores web, en caso de desarrollo web) proveer de APIs que le permitan ejecutar dichas tareas de manera asincrónica. + +## Código de bloqueo (Blocking) + +Las técnicas asincrónicas son muy útiles, particularmente en programación web. Cuando una app web se ejecuta en el navegador y ejecuta un gran bloque de código sin retornar el control al navegador, este mismo puede parecer que se congela. Esto es llamado **blocking**; el navegador es bloqueado para que el usuario pueda seguir interactuando y realizando otras tareas hasta que la app web retorne el control sobre el procesador. + +Vamos a ver algunos ejemplos que muestren lo que significa blocking. + +En nuestro ejemplo [simple-sync.html](https://github.com/mdn/learning-area/tree/master/javascript/asynchronous/introducing/simple-sync.html) ([véalo en vivo](https://mdn.github.io/learning-area/javascript/asynchronous/introducing/simple-sync.html)), agregamos un detector del evento click ("click event listener") a un botón con el fin de que cuando sea clickeado, ejecute una operación de un gran consumo de tiempo (calcula 10 millones de fechas y luego muestra la última en la consola) y luego agrega un párrafo al DOM: + +```js +const btn = document.querySelector('button'); +btn.addEventListener('click', () => { + let myDate; + for(let i = 0; i < 10000000; i++) { + let date = new Date(); + myDate = date + } + + console.log(myDate); + + let pElem = document.createElement('p'); + pElem.textContent = 'This is a newly-added paragraph.'; + document.body.appendChild(pElem); +}); +``` + +Cuando ejecute el ejemplo, abra su consola de JavaScript y haga click en el botón — notará que el párrafo no aparece hasta que las fechas hayan sido calculadas en su totalidad y el mensaje en la consola haya sido logueado. EL código se ejecuta en el orden en que aparece (de arriba hacia abajo), y la última operación no se ejecuta hasta que la anterior haya terminado. + +> **Nota:** El ejemplo anterior es poco realista. ¡Nunca se van a calcular 10 millones de fechas en una app web real! Sin embargo, sirve para dar una idea básica. + +En nuestro segundo ejemplo, [simple-sync-ui-blocking.html](https://github.com/mdn/learning-area/blob/master/javascript/asynchronous/introducing/simple-sync-ui-blocking.html) ([véalo en vivo](https://mdn.github.io/learning-area/javascript/asynchronous/introducing/simple-sync-ui-blocking.html)), se simula algo un poco más realista con el que se puede encontrar en una página real. Se bloquea la interacción del usuario con la carga ("rendering") de la UI. En este ejemplo, se tienen dos botones: + +- Un botón "Fill canvas" que cuando es clickeado llena con 1 millón de círculos azules al {{htmlelement("canvas")}} disponible. +- Un botón "Click me for alert" que cuando es clickeado muestra un mensaje de alerta. + +```js +function expensiveOperation() { + for(let i = 0; i < 1000000; i++) { + ctx.fillStyle = 'rgba(0,0,255, 0.2)'; + ctx.beginPath(); + ctx.arc(random(0, canvas.width), random(0, canvas.height), 10, degToRad(0), degToRad(360), false); + ctx.fill() + } +} + +fillBtn.addEventListener('click', expensiveOperation); + +alertBtn.addEventListener('click', () => + alert('You clicked me!') +); +``` + +Si se clickea el primer botón y rápidamente se clickea el segundo, se verá que la alerta no aparece hasta que los círculos hayan terminado de representarse. La primer operación blockea a la segunda hasta que esta haya terminado de ejecutarse. + +> **Nota:** OK, nuestro caso es feo y estamos fingiendo el efecto de bloqueo, pero es un problema común con el que los desarrolladores de aplicaciones reales batallan todo el tiempo. + +¿Por qué es esto? La respuesta es porque JavaScript, en general, es de **"un solo hilo" (single-threaded)**. En este punto, se tiene que introduce el concepto de **"hilos" (threads)**. + +## Threads + +Un **hilo (thread)** es básicamente un proceso simple que un programa puede usar para completar tareas ("tasks"). Cada hilo solo puede realizar una tarea a la vez: + + Task A --> Task B --> Task C + +Cada tarea se va a ejecutar secuencialmente; una tarea tiene que completarse antes de que la próxima empiece. + +Como se dijo previamente, muchas computadores actualmente tienen múltiples procesadores, por lo que pueden realizar múltiples tareas a la vez. Los lenguajes de programación que pueden manejar múltiples hilos pueden usar múltiples procesadores para completar múltiples tareas en simultáneo. + + Thread 1: Task A --> Task B + Thread 2: Task C --> Task D + +### JavaScript es single-threaded + +JavScript es tradicionalmente single-threaded. Aún con múltiples procesadores, solo se puede ejecutar tareas en un solo hilo, llamado el **hilo principal (main thread)**. El ejemplo de arriba se ejecuta de la siguiente manera: + + Main thread: Render circles to canvas --> Display alert() + +Después de un tiempo, JavaScript ganó algunas herramientas que ayudaron con dichos problemas. [Web workers](/es/docs/Web/API/Web_Workers_API) permiten que se envíe parte del procesamiento de JavaScript a un hilo separado, llamado worker con el fin de que puedan ejecutar múltiples pedazos de JavaScript en simultáneo. Generalmente se usará un worker para ejectuar procesos de mucho consumo del hilo principal (main thread) con el fin de que no se bloquee la interacción del usuario. + + Main thread: Task A --> Task C + Worker thread: Expensive task B + +Con esto en mente, miremos el ejemplo [simple-sync-worker.html](https://github.com/mdn/learning-area/blob/master/javascript/asynchronous/introducing/simple-sync-worker.html) ([véalo ejecutándose en vivo](https://mdn.github.io/learning-area/javascript/asynchronous/introducing/simple-sync-worker.html)) nuevamente con la consola de JavaScript del navegador abierta. Esto es una re-escritura del ejemplo anterior que calculaba 10 millones de fechas en hilos worker separados. Ahora si se clickea el botón, el navegador tiene permitido mostrar el párrafo antes de que las fechas haya terminado de calcularse. La primer operación ya no bloquea a la segunda. + +## Código asincrónico + +Los web workers son muy útiles, pero tienen limitaciones. La mayor es que no pueden acceder al {{Glossary("DOM")}} — no se puede logar que un worker modifique directamente algo de la UI. No se puede representar 1 millón de círculos azules en un worker; básicamente solo puede hacer el cálculo numérico. + +El segundo problema es que a pesar de que el código se ejecuta en un worker no es bloqueador, es simplemente sincrónico. Esto se convierte en un problema cuando una función depender en los resultados de múltiples procesos previos para funcionar. Considere el siguiente diagrama de hilos: + + Main thread: Task A --> Task B + +En este caso, digamos que la Tarea A (Task A) está haciendo algo como buscando una imagen de un servidor y la Tarea B (Task B) luego hace algo con la imagen, como aplicarle un filtro. Si se ejecuta la Tarea A y luego inmediatamente se trata de ejecutar la Tarea B, se obtendrá un error, porque la imagen todavía no estará disponible. + + Main thread: Task A --> Task B --> |Task D| + Worker thread: Task C -----------> | | + +En este caso, digamos que la Tarea D hace uso de los resultados de la Tarea B y la Tarea C. Se se puede garantizar que esos resultados estarán disponibles al mismo tiempo, entonces tal vez estemos OK, pero es poco probable. Si la Tarea D trata de ejecutarse cuando uno de sus inputs no está disponible, disparará un error. + +Para arreglar dichos problemas, los navegadores nos permiten ejecutar ciertas operaciones asincrónicamente. Características como las [Promises](/es/docs/Web/JavaScript/Reference/Global_Objects/Promise) (Promesas) permiten establecer la ejecución de una operación (por ejemplo, buscar una imagen desde un servidor), y luego esperar hasta que el resultado sea retornado antes de ejecutar otra operación. + + Main thread: Task A Task B + Promise: |__async operation__| + +Como la operación está sucediendo en otro lugar, el hilo principal no está bloqueado mientras la operación asincrónica está siendo procesada. + +Vamos a empezar a ver cómo se puede escribir código asincrónico en el próximo artículo. Cosas emocionantes, ¿eh? ¡Siga leyendo! + +## Conclusión + +El diseño del software moderno gira cada más entorno a la programación asincrónica, para permiterle a los programas hacer más de una cosa a la vez. A medida que use nuevas y más poderosas APIs, encontrará más casos donde la única forma de realizar las cosas es asincrónicamente. Era muy difícil escribir el código asincrónico. Todavía lleva tiempo acostumbrarse, pero se ha vuelto mucho más sencillo. En el resto de este módulo, exploraremos porqué el código asincrónico importa y cómo diseñar código que evite algunos de los problemas que hemos descrito en este artículo. + +## En este módulo + +- [Conceptos generales de programación asincrónica](/es/docs/Learn/JavaScript/Asynchronous/Concepts) +- [Introducción a JavaScript asincrónico](/es/docs/Learn/JavaScript/Asynchronous/Introducing) +- [JavaScript asincrónico cooperativo: Timeouts e intervalos](/es/docs/Learn/JavaScript/Asynchronous/Timeouts_and_intervals) +- [Programación asincrónica elegante con Promesas](/es/docs/Learn/JavaScript/Asynchronous/Promises) +- [Programación asincrónica más sencilla con async y await](/es/docs/Learn/JavaScript/Asynchronous/Async_await) +- [Eligiendo el correcto enfoque](/es/docs/Learn/JavaScript/Asynchronous/Choosing_the_right_approach) diff --git a/files/es/learn/javascript/building_blocks/build_your_own_function/index.html b/files/es/learn/javascript/building_blocks/build_your_own_function/index.html deleted file mode 100644 index 0c9000ed484f14..00000000000000 --- a/files/es/learn/javascript/building_blocks/build_your_own_function/index.html +++ /dev/null @@ -1,253 +0,0 @@ ---- -title: Construye tu propia función -slug: Learn/JavaScript/Building_blocks/Build_your_own_function -tags: - - Aprender - - Artículo - - Construir - - Funciones - - Guía - - JavaScript - - Principiante - - Tutorial -translation_of: Learn/JavaScript/Building_blocks/Build_your_own_function -original_slug: Learn/JavaScript/Building_blocks/Construyendo_tu_propia_funcion ---- -
{{LearnSidebar}}
- -
{{PreviousMenuNext("Learn/JavaScript/Building_blocks/Functions","Learn/JavaScript/Building_blocks/Return_values", "Learn/JavaScript/Building_blocks")}}
- -

Con la mayor parte de la teoría esencial tratada en el artículo anterior, este artículo proporciona experiencia práctica. Aquí obtendrás práctica construyendo tu propia función personalizada. En el camino, también explicaremos algunos detalles útiles sobre cómo tratar las funciones.

- - - - - - - - - - - - -
Prerequisites:Conocimientos básicos de computación, una comprensión básica de HTML y CSS, JavaScript first steps, Functions — reusable blocks of code.
Objective:Para proporcionar algo de práctica en la construcción de una función personalizada, y explicar algunos detalles asociados más útiles.
- -

Aprendizaje activo: construyamos una función.

- -

La función personalizada que vamos a construir se llamará displayMessage(). Mostrará un cuadro de mensaje personalizado en una página web y actuará como un reemplazo personalizado para la función de alert() incorporada de un navegador. Hemos visto esto antes, pero solo refresquemos nuestros recuerdos. Escriba lo siguiente en la consola de JavaScript de su navegador, en la página que desee:

- -
alert('This is a message');
- -

La función alert tiene un argumento — el string que se muestra en la alerta. Prueba a variar el string para cambiar el mensaje.

- -

La función alert es limitada: pueder cambiar el mensaje, pero no puedes cambiar de manera sencilla nada más, como el color, icono o cualquier otra cosa. Construiremos uno que resultará ser más divertido.

- -
-

Nota: Este ejemplo debería funcionar bien en todos los navegadores modernos, pero el estilo puede parecer un poco divertido en los navegadores un poco más antiguos. Te recomendamos que hagas este ejercicio en un navegador moderno como Firefox, Opera o Chrome.

-
- -

La función básica

- -

Para empezar, vamos a poner juntos una función básica.

- -
-

Nota: Para las convenciones de nombres de las funciones, debes seguir las mismas reglas que convecion de nombres de variables. Esto está bien, ya que puede distinguirlos: los nombres de las funciones aparecen entre paréntesis después de ellos y las variables no.

-
- -
    -
  1. Comience accediendo al archivo function-start.html y haciendo una copia local. Verás que el HTML es simple — el body unicamente tiene un botón. También hemos propocionado algunos estilos básicos de CSS para customizar el mensaje y un elemento {{htmlelement("script")}} vacío para poner nuestro JavaScript dentro.
  2. -
  3. Luego añade lo siguiente dentro del elemento <script>: -
    function displayMessage() {
    -
    -}
    - Comenzamos con la palabra clave función, lo que significa que estamos definiendo una función. A esto le sigue el nombre que queremos darle a nuestra función, un conjunto de paréntesis y un conjunto de llaves. Todos los parámetros que queremos darle a nuestra función van dentro de los paréntesis, y el código que se ejecuta cuando llamamos a la función va dentro de las llaves.
  4. -
  5. Finalmente, agregue el siguiente código dentro de las llaves: -
    let html = document.querySelector('html');
    -
    -let panel = document.createElement('div');
    -panel.setAttribute('class', 'msgBox');
    -html.appendChild(panel);
    -
    -let msg = document.createElement('p');
    -msg.textContent = 'This is a message box';
    -panel.appendChild(msg);
    -
    -let closeBtn = document.createElement('button');
    -closeBtn.textContent = 'x';
    -panel.appendChild(closeBtn);
    -
    -closeBtn.onclick = function() {
    -  panel.parentNode.removeChild(panel);
    -}
    -
  6. -
- -

Esto es un montón de código por el que pasar, así que lo guiaremos paso a paso.

- -

La primera línea usa un función DOM API llamada {{domxref("document.querySelector()")}} para seleccionar el elemento {{htmlelement("html")}} y guardar una referencia a él en una variable llamada html, por lo que podemos hacer cosas para más adelante:

- -
let html = document.querySelector('html');
- -

La siguiente sección usa otra función del DOM API llamada {{domxref("Document.createElement()")}} para crear un elemento {{htmlelement("div")}} y guardar una referencia a él en una variable llamada panel. Este elemento será el contenedor exterior de nuestro cuadro de mensaje.

- -

Entonces usamos otra función del API DOM llamada {{domxref("Element.setAttribute()")}} para configurar un atributo a class en nuestro panel con un valor de msgBox. Esto es para facilitar el estilo del elemento. — Si echas un vistazo al CSS en la página, verás que estamos utilizando un selector de clases.msgBox para dar estilo al al contenedor del mensaje.

- -

Finalmente, llamamos a una función del DOM llamada {{domxref("Node.appendChild()")}} en la variable html que hemos guardado anteriormente, que anida un elemento dentro del otro como hijo de él. Hemos especificado el panel <div> como el hijo que queremos añadir dentro del elemento <html>. Debemos hacer esto ya que el elemento que creamos no aparecerá en la página por sí solo — tenemos que especificar donde ponerlo.

- -
let panel = document.createElement('div');
-panel.setAttribute('class', 'msgBox');
-html.appendChild(panel);
- -

Las siguientes dos secciones hacen uso de las mismas funciones createElement() y appendChild() que ya vimos para crear dos nuevos elementos — un {{htmlelement("p")}} y un {{htmlelement("button")}} — e insertarlo en la página como un hijo del panel <div>. Usamos su propiedad {{domxref("Node.textContent")}} — que representa el contenido de texto de un elemento: para insertar un mensaje dentro del párrafo y una 'x' dentro del botón. Este botón será lo que necesita hacer clic / activar cuando el usuario quiera cerrar el cuadro de mensaje.

- -
let msg = document.createElement('p');
-msg.textContent = 'This is a message box';
-panel.appendChild(msg);
-
-let closeBtn = document.createElement('button');
-closeBtn.textContent = 'x';
-panel.appendChild(closeBtn);
- -

Finalmente, usamos el manejador de evento {{domxref("GlobalEventHandlers.onclick")}} para hacerlo de modo que cuando se haga clic en el botón, se ejecute algún código para eliminar todo el panel de la página, para cerrar el cuadro de mensaje.

- -

Brevemente, el handler onclick es una propiedad disponible en el botón (o, de hecho, en cualquier elemento de la página) que se puede configurar en una función para especificar qué código ejecutar cuando se hace clic en el botón. Aprenderás mucho más sobre esto en nuestro artículo de eventos posteriores. Estamos haciendo el handler onclick igual que una función anónima, que contiene el código para ejecutar cuando se ha hecho click en el botón. La línea dentro de la función usa la función del DOM API {{domxref("Node.removeChild()")}} para especificar que queremos eliminar un elemento secundario específico del elemento HTML— en este caso el panel <div>.

- -
closeBtn.onclick = function() {
-  panel.parentNode.removeChild(panel);
-}
- -

Básicamente, todo este bloque de código está generando un bloque de HTML que se ve así, y lo está insertando en la página:

- -
<div class="msgBox">
-  <p>This is a message box</p>
-  <button>x</button>
-</div>
- -

Fue un montón de código con el que trabajar: ¡no te preocupes demasiado si no recuerdas exactamente cómo funciona todo ahora! La parte principal en la que queremos centrarnos aquí es la estructura y el uso de la función, pero queríamos mostrar algo interesante para este ejemplo.

- -

Llamando a la función

- -

Ahora tienes la definición de tu función escrita en tu elemento <script> bien, pero no hará nada tal como está.

- -
    -
  1. Intente incluir la siguiente línea debajo de su función para llamarla: -
    displayMessage();
    - Esta línea invoca la función, haciéndola correr inmediatamente. Cuando guarde el código y lo vuelva a cargar en el navegador, verá que el pequeño cuadro de mensaje aparece inmediatamente, solo una vez. Después de todo, solo lo llamamos una vez.
  2. -
  3. -

    Ahora abra las herramientas de desarrollo de su navegador en la página de ejemplo, vaya a la consola de JavaScript y escriba la línea nuevamente allí, ¡verá que aparece nuevamente! Así que esto es divertido: ahora tenemos una función reutilizable que podemos llamar en cualquier momento que queramos.

    - -

    Pero probablemente queremos que aparezca en respuesta a las acciones del usuario y del sistema. En una aplicación real, tal cuadro de mensaje probablemente se llamará en respuesta a la disponibilidad de nuevos datos, a un error, al usuario que intenta eliminar su perfil ("¿está seguro de esto?"), O al usuario que agrega un nuevo contacto y la operación completando con éxito ... etc.

    - -

    En esta demostración, obtendremos el cuadro de mensaje que aparecerá cuando el usuario haga clic en el botón.

    -
  4. -
  5. Elimina la línea anterior que agregaste.
  6. -
  7. A continuación, seleccionaremos el botón y guardaremos una referencia a él en una variable. Agregue la siguiente línea a su código, encima de la definición de la función: -
    let btn = document.querySelector('button');
    -
  8. -
  9. Finalmente, agregue la siguiente línea debajo de la anterior: -
    btn.onclick = displayMessage;
    - De una forma similar que nuestra línea dentro de la función closeBtn.onclick..., aquí estamos llamando a algún código en respuesta a un botón al hacer clic. Pero en este caso, en lugar de llamar a una función anónima que contiene algún código, estamos llamando directamente a nuestro nombre de función.
  10. -
  11. Intente guardar y actualizar la página: ahora debería ver aparecer el cuadro de mensaje cuando hace clic en el botón.
  12. -
- -

Quizás te estés preguntando por qué no hemos incluido los paréntesis después del nombre de la función. Esto se debe a que no queremos llamar a la función inmediatamente, solo después de hacer clic en el botón. Si intentas cambiar la línea a

- -
btn.onclick = displayMessage();
- -

y al guardar y volver a cargar, verás que aparece el cuadro de mensaje sin hacer clic en el botón. Los paréntesis en este contexto a veces se denominan "operador de invocación de función". Solo los utiliza cuando desea ejecutar la función inmediatamente en el ámbito actual. Del mismo modo, el código dentro de la función anónima no se ejecuta inmediatamente, ya que está dentro del alcance de la función.

- -

Si has intentado el último experimento, asegúrate de deshacer el último cambio antes de continuar.

- -

Mejora de la función con parámetros.

- -

Tal como está, la función aún no es muy útil, no queremos mostrar el mismo mensaje predeterminado cada vez. Mejoremos nuestra función agregando algunos parámetros, permitiéndonos llamarla con algunas opciones diferentes.

- -
    -
  1. En primer lugar, actualice la primera línea de la función: -
    function displayMessage() {
    - to this: - -
    function displayMessage(msgText, msgType) {
    - Ahora, cuando llamamos a la función, podemos proporcionar dos valores variables dentro de los paréntesis para especificar el mensaje que se mostrará en el cuadro de mensaje y el tipo de mensaje que es.
  2. -
  3. Para utilizar el primer parámetro, actualiza la siguiente línea dentro de su función: -
    msg.textContent = 'This is a message box';
    - to - -
    msg.textContent = msgText;
    -
  4. -
  5. Por último, pero no menos importante, ahora necesita actualizar su llamada de función para incluir un texto de mensaje actualizado. Cambia la siguiente línea: -
    btn.onclick = displayMessage;
    - to this block: - -
    btn.onclick = function() {
    -  displayMessage('Woo, this is a different message!');
    -};
    - Si queremos especificar parámetros dentro de paréntesis para la función a la que estamos llamando, no podemos llamarla directamente, necesitamos colocarla dentro de una función anónima para que no esté en el ámbito inmediato y, por lo tanto, no se llame de inmediato. Ahora no se llamará hasta que se haga clic en el botón.
  6. -
  7. Vuelva a cargar e intenta el código nuevamente y verás que aún funciona bien, ¡excepto que ahora también puede variar el mensaje dentro del parámetro para obtener diferentes mensajes mostrados en el cuadro!
  8. -
- -

Un parámetro más complejo.

- -

En el siguiente parámetro. Este va a implicar un poco más de trabajo: lo configuraremos de modo que, dependiendo de la configuración del parámetro msgType, la función mostrará un icono diferente y un color de fondo diferente.

- -
    -
  1. En primer lugar, descargue los iconos necesarios para este ejercicio (warning y chat) de GitHub. Guárdalos en una nueva carpeta llamada icons en la misma localización que tu HTML. - -
    Nota: los iconos warning y chat que se encuentran en iconfinder.com, han sido diseñados por Nazarrudin Ansyari. Gracias!
    -
  2. -
  3. A continuación, encuentra el CSS dentro de tu archivo HTML. Haremos algunos cambios para dar paso a los iconos. Primero, actualiza el ancho de .msgBox desde: -
    width: 200px;
    - to - -
    width: 242px;
    -
  4. -
  5. Luego, añade las siguientes líneas dentro de la regla.msgBox p { ... }: -
    padding-left: 82px;
    -background-position: 25px center;
    -background-repeat: no-repeat;
    -
  6. -
  7. Ahora necesitamos añadir código a la función displayMessage() para manejar la visualización de los iconos. Agrega el siguiente bloque justo encima de la llave de cierre (}) de tu función : -
    if (msgType === 'warning') {
    -  msg.style.backgroundImage = 'url(icons/warning.png)';
    -  panel.style.backgroundColor = 'red';
    -} else if (msgType === 'chat') {
    -  msg.style.backgroundImage = 'url(icons/chat.png)';
    -  panel.style.backgroundColor = 'aqua';
    -} else {
    -  msg.style.paddingLeft = '20px';
    -}
    -
  8. -
  9. Aquí, si el parámetro msgType se establece como 'warning', se muestra el icono de advertencia y el color de fondo del panel se establece en rojo. Si se establece en 'chat', se muestra el icono de chat y el color de fondo del panel se establece en azul aguamarina. Si el parámetro msgType no está configurado en absoluto (o en algo diferente), entonces la parte else { ... } del código entra en juego, y al párrafo simplemente se le da un relleno predeterminado y ningún icono, sin el conjunto de colores del panel de fondo ya sea. Esto proporciona un estado predeterminado si no se proporciona ningún parámetro msgType , lo que significa que es un parámetro opcional.
  10. -
  11. Vamos a probar nuestra función actualizada , prueba a actualizar la llamada a displayMessage() con esto: -
    displayMessage('Woo, this is a different message!');
    - to one of these: - -
    displayMessage('Your inbox is almost full — delete some mails', 'warning');
    -displayMessage('Brian: Hi there, how are you today?','chat');
    - Puedes ver cuán útil se está volviendo nuestra (ahora no tan) poca función.
  12. -
- -
-

Nota: Si estas teniendo problemas con el ejemplo, sientente libre para coger el ejemplo para trabajar con él, finished version on GitHub (see it running live también), o pídenos ayuda.

-
- -

Conclusión

- -

¡Felicidades por llegar al final! Este artículo lo llevó a través de todo el proceso de creación de una función personalizada y práctica, que con un poco más de trabajo podría trasplantarse en un proyecto real. En el siguiente artículo resumiremos las funciones explicando otro concepto esencial relacionado: valores de retorno.

- - - -

{{PreviousMenuNext("Learn/JavaScript/Building_blocks/Functions","Learn/JavaScript/Building_blocks/Return_values", "Learn/JavaScript/Building_blocks")}}

- -

En este módulo

- - diff --git a/files/es/learn/javascript/building_blocks/build_your_own_function/index.md b/files/es/learn/javascript/building_blocks/build_your_own_function/index.md new file mode 100644 index 00000000000000..fb4fb7b79f212a --- /dev/null +++ b/files/es/learn/javascript/building_blocks/build_your_own_function/index.md @@ -0,0 +1,318 @@ +--- +title: Construye tu propia función +slug: Learn/JavaScript/Building_blocks/Build_your_own_function +tags: + - Aprender + - Artículo + - Construir + - Funciones + - Guía + - JavaScript + - Principiante + - Tutorial +translation_of: Learn/JavaScript/Building_blocks/Build_your_own_function +original_slug: Learn/JavaScript/Building_blocks/Construyendo_tu_propia_funcion +--- +{{LearnSidebar}}{{PreviousMenuNext("Learn/JavaScript/Building_blocks/Functions","Learn/JavaScript/Building_blocks/Return_values", "Learn/JavaScript/Building_blocks")}} + +Con la mayor parte de la teoría esencial tratada en el artículo anterior, este artículo proporciona experiencia práctica. Aquí obtendrás práctica construyendo tu propia función personalizada. En el camino, también explicaremos algunos detalles útiles sobre cómo tratar las funciones. + + + + + + + + + + + + +
Prerequisites: + Conocimientos básicos de computación, una comprensión básica de HTML y + CSS, + JavaScript first steps, + Functions — reusable blocks of code. +
Objective: + Para proporcionar algo de práctica en la construcción de una función + personalizada, y explicar algunos detalles asociados más útiles. +
+ +## Aprendizaje activo: construyamos una función. + +La función personalizada que vamos a construir se llamará `displayMessage()`. Mostrará un cuadro de mensaje personalizado en una página web y actuará como un reemplazo personalizado para la función de [alert()](/es/docs/Web/API/Window/alert) incorporada de un navegador. Hemos visto esto antes, pero solo refresquemos nuestros recuerdos. Escriba lo siguiente en la consola de JavaScript de su navegador, en la página que desee: + +```js +alert('This is a message'); +``` + +La función `alert` tiene un argumento — el string que se muestra en la alerta. Prueba a variar el string para cambiar el mensaje. + +La función `alert` es limitada: pueder cambiar el mensaje, pero no puedes cambiar de manera sencilla nada más, como el color, icono o cualquier otra cosa. Construiremos uno que resultará ser más divertido. + +> **Nota:** Este ejemplo debería funcionar bien en todos los navegadores modernos, pero el estilo puede parecer un poco divertido en los navegadores un poco más antiguos. Te recomendamos que hagas este ejercicio en un navegador moderno como Firefox, Opera o Chrome. + +## La función básica + +Para empezar, vamos a poner juntos una función básica. + +> **Nota:** Para las convenciones de nombres de las funciones, debes seguir las mismas reglas que [convecion de nombres de variables](/en-US/Learn/JavaScript/First_steps/Variables#An_aside_on_variable_naming_rules). Esto está bien, ya que puede distinguirlos: los nombres de las funciones aparecen entre paréntesis después de ellos y las variables no. + +1. Comience accediendo al archivo [function-start.html](https://github.com/mdn/learning-area/blob/master/javascript/building-blocks/functions/function-start.html) y haciendo una copia local. Verás que el HTML es simple — el body unicamente tiene un botón. También hemos propocionado algunos estilos básicos de CSS para customizar el mensaje y un elemento {{htmlelement("script")}} vacío para poner nuestro JavaScript dentro. +2. Luego añade lo siguiente dentro del elemento ` + + + +``` + +{{ EmbedLiveSample('Hidden_code', '100%', 400, "", "", "hide-codepen-jsfiddle") }} + +No tienes que entender todo el código por ahora, pero vamos a echar un vistazo a la parte de código que dibuja los 100 círculos: + +```js +for (var i = 0; i < 100; i++) { + ctx.beginPath(); + ctx.fillStyle = 'rgba(255,0,0,0.5)'; + ctx.arc(random(WIDTH), random(HEIGHT), random(50), 0, 2 * Math.PI); + ctx.fill(); +} +``` + +Debes quedarte con la idea básica. — utilizamos un bucle para ejecutar 100 iteracciones de este código, cada una de las cuales dibuja un círculo en una posición aleatoria de la página. La cantidad de código necesario sería el mismo si dibujáramos 100, 1000, o 10,000 círculos. Solo necesitamos cambiar un número. + +Si no usáramos un bucle aquí, tendríamos que repetir el siguiente código por cada círculo que quisiéramos dibujar: + +```js +ctx.beginPath(); +ctx.fillStyle = 'rgba(255,0,0,0.5)'; +ctx.arc(random(WIDTH), random(HEIGHT), random(50), 0, 2 * Math.PI); +ctx.fill(); +``` + +Esto sería muy aburrido y difícil de mantener de forma rápida. Los bucles son realmente lo mejor. + +## El bucle estándar `for` + +Exploremos algunos constructores de bucles específicos. El primero, que usarás la mayoría de las veces, es el bucle [for](/es/docs/Web/JavaScript/Referencia/Sentencias/for) - este tiene la siguiente sintaxis: + +```js +for (inicializador; condición de salida; expresión final) { + // código a ejecutar +} +``` + +Aquí tenemos: + +1. La palabra reservada `for`, seguida por algunos paréntesis. +2. Dentro de los paréntesis tenemos tres ítems, separados por punto y coma (;): + + 1. Un **inicializador** - Este es usualmente una variable con un número asignado, que aumenta el número de veces que el bucle ha sijo ejecutado. También se le llama **contador** o **variable de conteo**. + 2. Una **condición de salida** - como se mencionó previamente, ésta define cuando el bucle debería detenerse. Generalmente es una expresión que contiene un operador de comparación, una prueba para verificar ue la condición de término o salida ha sido cumplida. + 3. Una **expresión final** - que es siempre evaluada o ejecutada cada vez que el bucle ha completado una iteración. Usualmente sirve para modificar al contador (incrementando su valor o algunas veces disminuyendolo), para aproximarse a la condición de salida. + +3. Algunos corchetes curvos que contienen un bloque de código - este código se ejecutará cada vez que se repita el bucle. + +Observa un ejemplo real para poder entender esto más claramente. + +```js +var cats = ['Bill', 'Jeff', 'Pete', 'Biggles', 'Jasmin']; +var info = 'My cats are called '; +var para = document.querySelector('p'); + +for (var i = 0; i < cats.length; i++) { + info += cats[i] + ', '; +} + +para.textContent = info; +``` + +Esto nos da el siguiente resultado: + +```html hidden + + + + + Basic for loop example + + + + + +

+ + + + + +``` + +{{ EmbedLiveSample('Hidden_code_2', '100%', 60, "", "", "hide-codepen-jsfiddle") }} + +> **Nota:** Puedes encontrar este [ejemplo de código en GitHub](https://github.com/mdn/learning-area/blob/master/javascript/building-blocks/loops/basic-for.html) también (además puedes [verlo ejecutar en vivo](http://mdn.github.io/learning-area/javascript/building-blocks/loops/basic-for.html)). + +Esto muestra un bucle siendo usado para iterar sobre los elementos de un arreglo (matriz), y hacer algo con cada uno de ellos - un patrón muy común en JavaScript. Aquí: + +1. El iterador, `i`, inicia en `0` (`var i = 0`). +2. Se le ha dicho que debe ejecutarse hasta que no sea menor que la longitud del arreglo `cats`. Esto es importante - la condición de salida muestra la condicion bajo la cual el bucle seguirá iterando. Así, en este caso, mientras `i < cats.length` sea verdadero, el bucle seguirá ejecutándose. +3. Dentro del bucle, concatenamos el elemento del bucle actual (`cats[i]` es `cats[lo que sea i en ese momento]`) junto con una coma y un espacio, al final de la variable `info`. Así: + + 1. Durante la primera ejecución, `i = 0`, así `cats[0] + ', '` se concatenará con la información ("Bill, "). + 2. Durante la segunda ejecución, `i = 1`, así `cats[1] + ', '` agregará el siguiente nombre ("Jeff, "). + 3. Y así sucesivamente. Después de cada vez que se ejecute el bucle, se incrementará en 1 el valod de i (`i++`), entonces el proceso comenzará de nuevo. + +4. Cuando `i` sea igual a `cats.length`, el bucle se detendrá, y el navegador se moverá al siguiente segmento de código bajo el bucle. + +> **Nota:** Hemos programado la condición de salidad como `i < cats.length`, y no como `i <= cats.length`, porque los computadores cuentan desde 0, no 1 - inicializamos la variable i en 0, para llegar a `i = 4` (el índice del último elemento del arreglo). `cats.length` responde 5, ya que existen 5 elementos en el arreglo, pero no queremos que i = 5, dado que respondería `undefined` para el último elemento (no existe un elemento en el arreglo con un índice 5). for the last item (there is no array item with an index of 5). Por ello, queremos llegar a 1 menos que `cats.length` (`i <`), que no es lo mismo que `cats.length` (`i <=`). + +> **Nota:** Un error común con la condición de salida es utilizar el comparador "igual a" (`===`) en vez de "menor o igual a" (`<=`). Si queremos que nuestro bucle se ejecute hasta que `i = 5`, la condición de salida debería ser `i <= cats.length`. Si la declaramos `i === cats.length`, el bucle no debería ejecutarse , porque `i` no es igual a `5` en la primera iteración del bucle, por lo que debería detenerse inmediatamente. + +Un pequeño problema que se presenta es que la frase de salida final no está muy bien formada: + +> My cats are called Bill, Jeff, Pete, Biggles, Jasmin, + +Idealmente querríamos cambiar la concatenacion al final de la última iteracion del bucle, así no tendríamos una coma en el final de la frase. Bueno, no hay problema - podemos insertar un condicional dentro de nuestro bucle para solucionar este caso especial: + +```js +for (var i = 0; i < cats.length; i++) { + if (i === cats.length - 1) { + info += 'and ' + cats[i] + '.'; + } else { + info += cats[i] + ', '; + } +} +``` + +> **Nota:** You can find this [example code on GitHub](https://github.com/mdn/learning-area/blob/master/javascript/building-blocks/loops/basic-for-improved.html) too (also [see it running live](http://mdn.github.io/learning-area/javascript/building-blocks/loops/basic-for-improved.html)). + +> **Advertencia:** Con `for` - como con todos los bucles - debes estar seguro de que el inicializador es repetido hasta que eventualemtne alcance la condición de salida. Si no, el bucle seguirá repitiéndose indefinidamente, y puede que el navegador lo fuerce a detenerse o se interrumpa. Esto se denomina **bucle infinito**. + +## Salir de un bucle con `break` + +Si deseas salir de un bucle antes de que se hayan completado todas las iteraciones, puedes usar la declaración [break](/es/docs/Web/JavaScript/Referencia/Sentencias/break). Ya la vimos en el artículo previo cuando revisamos la declaración [switch](/es/docs/Web/JavaScript/Referencia/Sentencias/switch) - cuando un caso en una declaración `switch` coincide con la expresión de entrada, la declaración `break` inmediatamente sale de la declaración `switch` y avanza al código que se encuentra después. + +Ocurre lo mismo con los bucles - una declaración `break` saldrá inmediatamente del bucle y hará que el navegador siga con el código que sigue después. + +Digamos que queremos buscar a través de un arreglo de contactos y números telefónicos y retornar sólo el número que queríamos encontrar. primero, un simple HTML - un {{htmlelement("input")}} de texto que nos permita ingresar un nombre para buscar, un elemento {{htmlelement("button")}} para enviar la búsqueda, y un elemento {{htmlelement("p")}} para mostrar el resultado: + +```html + + + + +

+``` + +Ahora en el JavaScript: + +```js +var contacts = ['Chris:2232322', 'Sarah:3453456', 'Bill:7654322', 'Mary:9998769', 'Dianne:9384975']; +var para = document.querySelector('p'); +var input = document.querySelector('input'); +var btn = document.querySelector('button'); + +btn.addEventListener('click', function() { + var searchName = input.value; + input.value = ''; + input.focus(); + for (var i = 0; i < contacts.length; i++) { + var splitContact = contacts[i].split(':'); + if (splitContact[0] === searchName) { + para.textContent = splitContact[0] + '\'s number is ' + splitContact[1] + '.'; + break; + } else { + para.textContent = 'Contact not found.'; + } + } +}); +``` + +```html hidden + + + + + Simple contact search example + + + + + + + + + +

+ + + + + +``` + +{{ EmbedLiveSample('Hidden_code_3', '100%', 100, "", "", "hide-codepen-jsfiddle") }} + +1. First of all we have some variable definitions — we have an array of contact information, with each item being a string containing a name and phone number separated by a colon. +2. Next, we attach an event listener to the button (`btn`), so that when it is pressed, some code is run to perform the search and return the results. +3. We store the value entered into the text input in a variable called `searchName`, before then emptying the text input and focusing it again, ready for the next search. +4. Now onto the interesting part, the for loop: + + 1. We start the counter at `0`, run the loop until the counter is no longer less than `contacts.length`, and increment `i` by 1 after each iteration of the loop. + 2. Inside the loop we first split the current contact (`contacts[i]`) at the colon character, and store the resulting two values in an array called `splitContact`. + 3. We then use a conditional statement to test whether `splitContact[0]` (the contact's name) is equal to the inputted `searchName`. If it is, we enter a string into the paragraph to report what the contact's number is, and use `break` to end the loop. + +5. If the contact name does not match the entered search, the paragraph text is set to "Contact not found.", and the loop continues iterating. + +> **Nota:** Note: You can view the [full source code on GitHub](https://github.com/mdn/learning-area/blob/master/javascript/building-blocks/loops/contact-search.html) too (also [see it running live](http://mdn.github.io/learning-area/javascript/building-blocks/loops/contact-search.html)). + +## Skipping iterations with continue + +The [continue](/es/docs/Web/JavaScript/Reference/Statements/continue) statement works in a similar manner to `break`, but instead of breaking out of the loop entirely, it skips to the next iteration of the loop. Let's look at another example that takes a number as an input, and returns only the numbers that are squares of integers (whole numbers). + +The HTML is basically the same as the last example — a simple text input, and a paragraph for output. The JavaScript is mostly the same too, although the loop itself is a bit different: + +```js +var num = input.value; + +for (var i = 1; i <= num; i++) { + var sqRoot = Math.sqrt(i); + if (Math.floor(sqRoot) !== sqRoot) { + continue; + } + + para.textContent += i + ' '; +} +``` + +Here's the output: + +```html hidden + + + + + Integer squares generator + + + + + + + + + +

Output:

+ + + + + +``` + +{{ EmbedLiveSample('Hidden_code_4', '100%', 100, "", "", "hide-codepen-jsfiddle") }} + +1. In this case, the input should be a number (`num`). The `for` loop is given a counter starting at 1 (as we are not interested in 0 in this case), an exit condition that says the loop will stop when the counter becomes bigger than the input `num`, and an iterator that adds 1 to the counter each time. +2. Inside the loop, we find the square root of each number using [Math.sqrt(i)](/es/docs/Web/JavaScript/Reference/Global_Objects/Math/sqrt), then check whether the square root is an integer by testing whether it is the same as itself when it has been rounded down to the nearest integer (this is what [Math.floor()](/es/docs/Web/JavaScript/Reference/Global_Objects/Math/floor) does to the number it is passed). +3. If the square root and the rounded down square root do not equal one another (`!==`), it means that the square root is not an integer, so we are not interested in it. In such a case, we use the `continue` statement to skip on to the next loop iteration without recording the number anywhere. +4. If the square root IS an integer, we skip past the if block entirely so the `continue` statement is not executed; instead, we concatenate the current `i` value plus a space on to the end of the paragraph content. + +> **Nota:** You can view the [full source code on GitHub](https://github.com/mdn/learning-area/blob/master/javascript/building-blocks/loops/integer-squares.html) too (also [see it running live](http://mdn.github.io/learning-area/javascript/building-blocks/loops/integer-squares.html)). + +## while and do ... while + +`for` is not the only type of loop available in JavaScript. There are actually many others and, while you don't need to understand all of these now, it is worth having a look at the structure of a couple of others so that you can recognize the same features at work in a slightly different way. + +First, let's have a look at the [while](/es/docs/Web/JavaScript/Reference/Statements/while) loop. This loop's syntax looks like so: + +```js +initializer +while (exit-condition) { + // code to run + + final-expression +} +``` + +This works in a very similar way to the for loop, except that the initializer variable is set before the loop, and the final-expression is included inside the loop after the code to run — rather than these two items being included inside the parentheses. The exit-condition is included inside the parentheses, which are preceded by the `while` keyword rather than `for`. + +The same three items are still present, and they are still defined in the same order as they are in the for loop — this makes sense, as you still have to have an initializer defined before you can check whether it has reached the exit-condition; the final-condition is then run after the code inside the loop has run (an iteration has been completed), which will only happen if the exit-condition has still not been reached. + +Let's have a look again at our cats list example, but rewritten to use a while loop: + +```js +var i = 0; + +while (i < cats.length) { + if (i === cats.length - 1) { + info += 'and ' + cats[i] + '.'; + } else { + info += cats[i] + ', '; + } + + i++; +} +``` + +> **Nota:** This still works just the same as expected — have a look at it [running live on GitHub](http://mdn.github.io/learning-area/javascript/building-blocks/loops/while.html) (also view the [full source code](https://github.com/mdn/learning-area/blob/master/javascript/building-blocks/loops/while.html)). + +The [do...while](/es/docs/Web/JavaScript/Reference/Statements/do...while) loop is very similar, but provides a variation on the while structure: + +```js +initializer +do { + // code to run + + final-expression +} while (exit-condition) +``` + +In this case, the initializer again comes first, before the loop starts. The `do` keyword directly precedes the curly braces containing the code to run and the final-expression. + +The differentiator here is that the exit-condition comes after everything else, wrapped in parentheses and preceded by a `while` keyword. In a `do...while` loop, the code inside the curly braces is always run once before the check is made to see if it should be executed again (in while and for, the check comes first, so the code might never be executed). + +Let's rewrite our cat listing example again to use a `do...while` loop: + +```js +var i = 0; + +do { + if (i === cats.length - 1) { + info += 'and ' + cats[i] + '.'; + } else { + info += cats[i] + ', '; + } + + i++; +} while (i < cats.length); +``` + +> **Nota:** Again, this works just the same as expected — have a look at it [running live on GitHub](http://mdn.github.io/learning-area/javascript/building-blocks/loops/do-while.html) (also view the [full source code](https://github.com/mdn/learning-area/blob/master/javascript/building-blocks/loops/do-while.html)). + +> **Advertencia:** With while and do...while — as with all loops — you must make sure that the initializer is iterated so that it eventually reaches the exit condition. If not, the loop will go on forever, and either the browser will force it to stop, or it will crash. This is called an **infinite loop**. + +## Active learning: Launch countdown! + +In this exercise, we want you to print out a simple launch countdown to the output box, from 10 down to Blast off. Specifically, we want you to: + +- Loop from 10 down to 0. We've provided you with an initializer — `var i = 10;`. +- For each iteration, create a new paragraph and append it to the output `
`, which we've selected using `var output = document.querySelector('.output');`. In comments, we've provided you with three code lines that need to be used somewhere inside the loop: + + - `var para = document.createElement('p');` — creates a new paragraph. + - `output.appendChild(para);` — appends the paragraph to the output `
`. + - `para.textContent =` — makes the text inside the paragraph equal to whatever you put on the right hand side, after the equals sign. + +- Different iteration numbers require different text to be put in the paragraph for that iteration (you'll need a conditional statement and multiple `para.textContent =` lines): + + - If the number is 10, print "Countdown 10" to the paragraph. + - If the number is 0, print "Blast off!" to the paragraph. + - For any other number, print just the number to the paragraph. + +- Remember to include an iterator! However, in this example we are counting down after each iteration, not up, so you **don't** want `i++` — how do you iterate downwards? + +If you make a mistake, you can always reset the example with the "Reset" button. If you get really stuck, press "Show solution" to see a solution. + +```html hidden +

Live output

+
+ +
+ +

Editable code

+

Press Esc to move focus away from the code area (Tab inserts a tab character).

+ + +
+ + +
+``` + +```css hidden +html { + font-family: sans-serif; +} + +h2 { +font-size: 16px; +} + +.a11y-label { +margin: 0; +text-align: right; +font-size: 0.7rem; +width: 98%; +} + +body { +margin: 10px; +background: #f5f9fa; +} +``` + +```js hidden +var textarea = document.getElementById('code'); +var reset = document.getElementById('reset'); +var solution = document.getElementById('solution'); +var code = textarea.value; +var userEntry = textarea.value; + +function updateCode() { +eval(textarea.value); +} + +reset.addEventListener('click', function() { +textarea.value = code; +userEntry = textarea.value; +solutionEntry = jsSolution; +solution.value = 'Show solution'; +updateCode(); +}); + +solution.addEventListener('click', function() { +if(solution.value === 'Show solution') { +textarea.value = solutionEntry; +solution.value = 'Hide solution'; +} else { +textarea.value = userEntry; +solution.value = 'Show solution'; +} +updateCode(); +}); + +var jsSolution = 'var output = document.querySelector(\'.output\');\noutput.innerHTML = \'\';\n\nvar i = 10;\n\nwhile(i >= 0) {\n var para = document.createElement(\'p\');\n if(i === 10) {\n para.textContent = \'Countdown \' + i;\n } else if(i === 0) {\n para.textContent = \'Blast off!\';\n } else {\n para.textContent = i;\n }\n\n output.appendChild(para);\n\n i--;\n}'; +var solutionEntry = jsSolution; + +textarea.addEventListener('input', updateCode); +window.addEventListener('load', updateCode); + +// stop tab key tabbing out of textarea and +// make it write a tab at the caret position instead + +textarea.onkeydown = function(e){ +if (e.keyCode === 9) { +e.preventDefault(); +insertAtCaret('\t'); +} + +if (e.keyCode === 27) { +textarea.blur(); +} +}; + +function insertAtCaret(text) { +var scrollPos = textarea.scrollTop; +var caretPos = textarea.selectionStart; + +var front = (textarea.value).substring(0, caretPos); +var back = (textarea.value).substring(textarea.selectionEnd, textarea.value.length); +textarea.value = front + text + back; +caretPos = caretPos + text.length; +textarea.selectionStart = caretPos; +textarea.selectionEnd = caretPos; +textarea.focus(); +textarea.scrollTop = scrollPos; +} + +// Update the saved userCode every time the user updates the text area code + +textarea.onkeyup = function(){ +// We only want to save the state when the user code is being shown, +// not the solution, so that solution is not saved over the user code +if(solution.value === 'Show solution') { +userEntry = textarea.value; +} else { +solutionEntry = textarea.value; +} + +updateCode(); +}; +``` + +{{ EmbedLiveSample('Active_learning', '100%', 880, "", "", "hide-codepen-jsfiddle") }} + +## Active learning: Filling in a guest list + +In this exercise, we want you to take a list of names stored in an array, and put them into a guest list. But it's not quite that easy — we don't want to let Phil and Lola in because they are greedy and rude, and always eat all the food! We have two lists, one for guests to admit, and one for guests to refuse. + +Specifically, we want you to: + +- Write a loop that will iterate from 0 to the length of the `people` array. You'll need to start with an initializer of `var i = 0;`, but what exit condition do you need? +- During each loop iteration, check if the current array item is equal to "Phil" or "Lola" using a conditional statement: + + - If it is, concatenate the array item to the end of the `refused` paragraph's `textContent`, followed by a comma and a space. + - If it isn't, concatenate the array item to the end of the `admitted` paragraph's `textContent`, followed by a comma and a space. + +We've already provided you with: + +- `var i = 0;` — Your initializer. +- `refused.textContent +=` — the beginnings of a line that will concatenate something on to the end of `refused.textContent`. +- `admitted.textContent +=` — the beginnings of a line that will concatenate something on to the end of `admitted.textContent`. + +Extra bonus question — after completing the above tasks successfully, you will be left with two lists of names, separated by commas, but they will be untidy — there will be a comma at the end of each one. Can you work out how to write lines that slice the last comma off in each case, and add a full stop to the end? Have a look at the [Useful string methods](/es/docs/Learn/JavaScript/First_steps/Useful_string_methods) article for help. + +If you make a mistake, you can always reset the example with the "Reset" button. If you get really stuck, press "Show solution" to see a solution. + +```html hidden +

Live output

+
+

Admit:

+

Refuse:

+
+ +

Editable code

+

Press Esc to move focus away from the code area (Tab inserts a tab character).

+ + +
+ + +
+``` + +```css hidden +html { + font-family: sans-serif; +} + +h2 { +font-size: 16px; +} + +.a11y-label { +margin: 0; +text-align: right; +font-size: 0.7rem; +width: 98%; +} + +body { +margin: 10px; +background: #f5f9fa; +} +``` + +```js hidden +var textarea = document.getElementById('code'); +var reset = document.getElementById('reset'); +var solution = document.getElementById('solution'); +var code = textarea.value; +var userEntry = textarea.value; + +function updateCode() { +eval(textarea.value); +} + +reset.addEventListener('click', function() { +textarea.value = code; +userEntry = textarea.value; +solutionEntry = jsSolution; +solution.value = 'Show solution'; +updateCode(); +}); + +solution.addEventListener('click', function() { +if(solution.value === 'Show solution') { +textarea.value = solutionEntry; +solution.value = 'Hide solution'; +} else { +textarea.value = userEntry; +solution.value = 'Show solution'; +} +updateCode(); +}); + +var jsSolution = 'var people = [\'Chris\', \'Anne\', \'Colin\', \'Terri\', \'Phil\', \'Lola\', \'Sam\', \'Kay\', \'Bruce\'];\n\nvar admitted = document.querySelector(\'.admitted\');\nvar refused = document.querySelector(\'.refused\');\n\nadmitted.textContent = \'Admit: \';\nrefused.textContent = \'Refuse: \'\nvar i = 0;\n\ndo {\n if(people[i] === \'Phil\' || people[i] === \'Lola\') {\n refused.textContent += people[i] + \', \';\n } else {\n admitted.textContent += people[i] + \', \';\n }\n i++;\n} while(i < people.length);\n\nrefused.textContent = refused.textContent.slice(0,refused.textContent.length-2) + \'.\';\nadmitted.textContent = admitted.textContent.slice(0,admitted.textContent.length-2) + \'.\';'; +var solutionEntry = jsSolution; + +textarea.addEventListener('input', updateCode); +window.addEventListener('load', updateCode); + +// stop tab key tabbing out of textarea and +// make it write a tab at the caret position instead + +textarea.onkeydown = function(e){ +if (e.keyCode === 9) { +e.preventDefault(); +insertAtCaret('\t'); +} + +if (e.keyCode === 27) { +textarea.blur(); +} +}; + +function insertAtCaret(text) { +var scrollPos = textarea.scrollTop; +var caretPos = textarea.selectionStart; + +var front = (textarea.value).substring(0, caretPos); +var back = (textarea.value).substring(textarea.selectionEnd, textarea.value.length); +textarea.value = front + text + back; +caretPos = caretPos + text.length; +textarea.selectionStart = caretPos; +textarea.selectionEnd = caretPos; +textarea.focus(); +textarea.scrollTop = scrollPos; +} + +// Update the saved userCode every time the user updates the text area code + +textarea.onkeyup = function(){ +// We only want to save the state when the user code is being shown, +// not the solution, so that solution is not saved over the user code +if(solution.value === 'Show solution') { +userEntry = textarea.value; +} else { +solutionEntry = textarea.value; +} + +updateCode(); +}; +``` + +{{ EmbedLiveSample('Active_learning_2', '100%', 680, "", "", "hide-codepen-jsfiddle") }} + +## Which loop type should you use? + +For basic uses, `for`, `while`, and `do...while` loops are largely interchangeable. They can all be used to solve the same problems, and which one you use will largely depend on your personal preference — which one you find easiest to remember or most intuitive. Let's have a look at them again. + +First `for`: + +```js +for (initializer; exit-condition; final-expression) { + // code to run +} +``` + +`while`: + +```js +initializer +while (exit-condition) { + // code to run + + final-expression +} +``` + +and finally `do...while`: + +```js +initializer +do { + // code to run + + final-expression +} while (exit-condition) +``` + +We would recommend `for`, at least to begin with, as it is probably the easiest for remembering everything — the initializer, exit-condition, and final-expression all have to go neatly into the parentheses, so it is easy to see where they are and check that you aren't missing them. + +> **Nota:** There are other loop types/features too, which are useful in advanced/specialized situations and beyond the scope of this article. If you want to go further with your loop learning, read our advanced [Loops and iteration guide](/es/docs/Web/JavaScript/Guide/Loops_and_iteration). + +## Conclusion + +This article has revealed to you the basic concepts behind, and different options available when, looping code in JavaScript. You should now be clear on why loops are a good mechanism for dealing with repetitive code, and be raring to use them in your own examples! + +If there is anything you didn't understand, feel free to read through the article again, or [contact us](/en-US/Learn#Contact_us) to ask for help. + +## See also + +- [Loops and iteration in detail](/es/docs/Web/JavaScript/Guide/Loops_and_iteration) +- [for statement reference](/es/docs/Web/JavaScript/Reference/Statements/for) +- [while](/es/docs/Web/JavaScript/Reference/Statements/while) and [do...while](/es/docs/Web/JavaScript/Reference/Statements/do...while) references +- [break](/es/docs/Web/JavaScript/Reference/Statements/break) and [continue](/es/docs/Web/JavaScript/Reference/Statements/continue) references +- [What’s the Best Way to Write a JavaScript For Loop?](https://www.impressivewebs.com/javascript-for-loop/) — some advanced loop best practices + +{{PreviousMenuNext("Learn/JavaScript/Building_blocks/conditionals","Learn/JavaScript/Building_blocks/Functions", "Learn/JavaScript/Building_blocks")}} + +## In this module + +- [Making decisions in your code — conditionals](/es/docs/Learn/JavaScript/Building_blocks/conditionals) +- [Looping code](/es/docs/Learn/JavaScript/Building_blocks/Looping_code) +- [Functions — reusable blocks of code](/es/docs/Learn/JavaScript/Building_blocks/Functions) +- [Build your own function](/es/docs/Learn/JavaScript/Building_blocks/Build_your_own_function) +- [Function return values](/es/docs/Learn/JavaScript/Building_blocks/Return_values) +- [Introduction to events](/es/docs/Learn/JavaScript/Building_blocks/Events) +- [Image gallery](/es/docs/Learn/JavaScript/Building_blocks/Image_gallery) diff --git a/files/es/learn/javascript/building_blocks/return_values/index.html b/files/es/learn/javascript/building_blocks/return_values/index.html deleted file mode 100644 index 2602a7217fa214..00000000000000 --- a/files/es/learn/javascript/building_blocks/return_values/index.html +++ /dev/null @@ -1,168 +0,0 @@ ---- -title: Una función retorna valores -slug: Learn/JavaScript/Building_blocks/Return_values -translation_of: Learn/JavaScript/Building_blocks/Return_values ---- -
{{LearnSidebar}}
- -
{{PreviousMenuNext("Learn/JavaScript/Building_blocks/Build_your_own_function","Learn/JavaScript/Building_blocks/Events", "Learn/JavaScript/Building_blocks")}}
- -

Hay un último concepto esencial para que discutamos en este curso, para cerrar nuestra visión de las funciones: — lo valores que se devuelven. Algunas funciones no devuelven un valor significativo después de su finalización, pero otras sí, y es importante comprender cuáles son sus valores, cómo utilizarlos en su código y cómo hacer que sus propias funciones personalizadas devuelvan valores útiles. Cubriremos todos estos a continuación.

- - - - - - - - - - - - -
Prerequisites: -

Basic computer literacy, a basic understanding of HTML and CSS, JavaScript first steps, Functions — reusable blocks of code.

-
Objective:To understand function return values, and how to make use of them.
- -

¿Qué son los valores de retorno?

- -

Los valores de retorno son exactamente como suenan: los valores devueltos por la función cuando se completa. Ya has alcanzado los valores de retorno varias veces, aunque es posible que no hayas pensado en ellos explícitamente. Volvamos a un código familiar:

- -
var myText = 'I am a string';
-var newString = myText.replace('string', 'sausage');
-console.log(newString);
-// la función de cadena replace () toma una cadena,
-// sustituyendo una subcadena con otra y devoviendo
-// ​​una cadena nueva con la sustitución realizada
- -

Vimos exactamente este bloque de código en nuestro primer artículo de función. Estamos invocando la función replace () en la cadena myText, y le pasamos dos parámetros: la subcadena a encontrar y la subcadena con la que reemplazarla. Cuando esta función se completa (termina de ejecutarse), devuelve un valor, que es una nueva cadena con el reemplazo realizado. En el código anterior, estamos guardando este valor de retorno como el valor de la variable newString.

- -

Si observa la página de referencia MDN de la función de reemplazo, verá una sección llamada Valor de retorno. Es muy útil conocer y comprender qué valores devuelven las funciones, por lo que tratamos de incluir esta información siempre que sea posible.

- -

Algunas funciones no devuelven un valor de retorno como tal (en nuestras páginas de referencia, el valor de retorno aparece como void o undefined en tales casos). Por ejemplo, en la función displayMessage () que creamos en el artículo anterior, no se devuelve ningún valor específico como resultado de la función que se invoca. Simplemente hace que aparezca un cuadro en algún lugar de la pantalla, ¡eso es todo!

- -

Generalmente, se usa un valor de retorno donde la función es un paso intermedio en un cálculo de algún tipo. Quieres llegar a un resultado final, que involucra algunos valores. Esos valores deben ser calculados por una función, que luego devuelve los resultados para que puedan usarse en la siguiente etapa del cálculo.

- -

Using return values in your own functions

- -

To return a value from a custom function, you need to use ... wait for it ... the return keyword. We saw this in action recently in our random-canvas-circles.html example. Our draw() function draws 100 random circles somewhere on an HTML {{htmlelement("canvas")}}:

- -
function draw() {
-  ctx.clearRect(0,0,WIDTH,HEIGHT);
-  for (var i = 0; i < 100; i++) {
-    ctx.beginPath();
-    ctx.fillStyle = 'rgba(255,0,0,0.5)';
-    ctx.arc(random(WIDTH), random(HEIGHT), random(50), 0, 2 * Math.PI);
-    ctx.fill();
-  }
-}
- -

Inside each loop iteration, three calls are made to the random() function, to generate a random value for the current circle's x coordinate, y coordinate, and radius, respectively. The random() function takes one parameter — a whole number — and it returns a whole random number between 0 and that number. It looks like this:

- -
function randomNumber(number) {
-  return Math.floor(Math.random()*number);
-}
- -

This could be written as follows:

- -
function randomNumber(number) {
-  var result = Math.floor(Math.random()*number);
-  return result;
-}
- -

But the first version is quicker to write, and more compact.

- -

We are returning the result of the calculation Math.floor(Math.random()*number) each time the function is called. This return value appears at the point the function was called, and the code continues. So for example, if we ran the following line:

- -
ctx.arc(random(WIDTH), random(HEIGHT), random(50), 0, 2 * Math.PI);
- -

and the three random() calls returned the values 500, 200, and 35, respectively, the line would actually be run as if it were this:

- -
ctx.arc(500, 200, 35, 0, 2 * Math.PI);
- -

The function calls on the line are run first and their return values substituted for the function calls, before the line itself is then executed.

- -

Active learning: our own return value function

- -

Let's have a go at writing our own functions featuring return values.

- -
    -
  1. First of all, make a local copy of the function-library.html file from GitHub. This is a simple HTML page containing a text {{htmlelement("input")}} field and a paragraph. There's also a {{htmlelement("script")}} element in which we have stored a reference to both HTML elements in two variables. This little page will allow you to enter a number into the text box, and display different numbers related to it in the paragraph below.
  2. -
  3. Let's add some useful functions to this <script> element. Below the existing two lines of JavaScript, add the following function definitions: -
    function squared(num) {
    -  return num * num;
    -}
    -
    -function cubed(num) {
    -  return num * num * num;
    -}
    -
    -function factorial(num) {
    -  var x = num;
    -  while (x > 1) {
    -    num *= x-1;
    -    x--;
    -  }
    -  return num;
    -}
    - The squared() and cubed() functions are fairly obvious — they return the square or cube of the number given as a parameter. The factorial() function returns the factorial of the given number.
  4. -
  5. Next, we're going to include a way to print out information about the number entered into the text input. Enter the following event handler below the existing functions: -
    input.onchange = function() {
    -  var num = input.value;
    -  if (isNaN(num)) {
    -    para.textContent = 'You need to enter a number!';
    -  } else {
    -    para.textContent = num + ' squared is ' + squared(num) + '. ' +
    -                       num + ' cubed is ' + cubed(num) + '. ' +
    -                       num + ' factorial is ' + factorial(num) + '.';
    -  }
    -}
    - -

    Here we are creating an onchange event handler that runs whenever the change event fires on the text input — that is, when a new value is entered into the text input, and submitted (enter a value then press tab for example). When this anonymous function runs, the existing value entered into the input is stored in the num variable.

    - -

    Next, we do a conditional test — if the entered value is not a number, we print an error message into the paragraph. The test looks at whether the expression isNaN(num) returns true. We use the isNaN() function to test whether the num value is not a number — if so, it returns true, and if not, false.

    - -

    If the test returns false, the num value is a number, so we print out a sentence inside the paragraph element stating what the square, cube, and factorial of the number are. The sentence calls the squared(), cubed(), and factorial() functions to get the required values.

    -
  6. -
  7. Save your code, load it in a browser, and try it out.
  8. -
- -
-

Note: If you have trouble getting the example to work, feel free to check your code against the finished version on GitHub (see it running live also), or ask us for help.

-
- -

At this point, we'd like you to have a go at writing out a couple of functions of your own and adding them to the library. How about the square or cube root of the number, or the circumference of a circle with a radius of length num?

- -

This exercise has brought up a couple of important points besides being a study on how to use the return statement. In addition, we have:

- - - -

Conclusion

- -

So there we have it — functions are fun, very useful and, although there's a lot to talk about in regards to their syntax and functionality, fairly understandable given the right articles to study.

- -

If there is anything you didn't understand, feel free to read through the article again, or contact us to ask for help.

- -

See also

- - - -

{{PreviousMenuNext("Learn/JavaScript/Building_blocks/Build_your_own_function","Learn/JavaScript/Building_blocks/Events", "Learn/JavaScript/Building_blocks")}}

- -

In this module

- - diff --git a/files/es/learn/javascript/building_blocks/return_values/index.md b/files/es/learn/javascript/building_blocks/return_values/index.md new file mode 100644 index 00000000000000..8c9ca361a6dfaa --- /dev/null +++ b/files/es/learn/javascript/building_blocks/return_values/index.md @@ -0,0 +1,186 @@ +--- +title: Una función retorna valores +slug: Learn/JavaScript/Building_blocks/Return_values +translation_of: Learn/JavaScript/Building_blocks/Return_values +--- +{{LearnSidebar}}{{PreviousMenuNext("Learn/JavaScript/Building_blocks/Build_your_own_function","Learn/JavaScript/Building_blocks/Events", "Learn/JavaScript/Building_blocks")}} + +Hay un último concepto esencial para que discutamos en este curso, para cerrar nuestra visión de las funciones: — lo valores que se devuelven. Algunas funciones no devuelven un valor significativo después de su finalización, pero otras sí, y es importante comprender cuáles son sus valores, cómo utilizarlos en su código y cómo hacer que sus propias funciones personalizadas devuelvan valores útiles. Cubriremos todos estos a continuación. + + + + + + + + + + + + +
Prerequisites: +

+ Basic computer literacy, a basic understanding of HTML and CSS, + JavaScript first steps, + Functions — reusable blocks of code. +

+
Objective: + To understand function return values, and how to make use of them. +
+ +## ¿Qué son los valores de retorno? + +**Los valores de retorno** son exactamente como suenan: los valores devueltos por la función cuando se completa. Ya has alcanzado los valores de retorno varias veces, aunque es posible que no hayas pensado en ellos explícitamente. Volvamos a un código familiar: + +```js +var myText = 'I am a string'; +var newString = myText.replace('string', 'sausage'); +console.log(newString); +// la función de cadena replace () toma una cadena, +// sustituyendo una subcadena con otra y devoviendo +// una cadena nueva con la sustitución realizada +``` + +Vimos exactamente este bloque de código en nuestro primer artículo de función. Estamos invocando la función [replace ()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace) en la cadena `myText`, y le pasamos dos parámetros: la subcadena a encontrar y la subcadena con la que reemplazarla. Cuando esta función se completa (termina de ejecutarse), devuelve un valor, que es una nueva cadena con el reemplazo realizado. En el código anterior, estamos guardando este valor de retorno como el valor de la variable `newString`. + +Si observa la página de referencia MDN de la función de reemplazo, verá una sección llamada [Valor de retorno.](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace#Return_value) Es muy útil conocer y comprender qué valores devuelven las funciones, por lo que tratamos de incluir esta información siempre que sea posible. + +Algunas funciones no devuelven un valor de retorno como tal (en nuestras páginas de referencia, el valor de retorno aparece como `void` o `undefined` en tales casos). Por ejemplo, en la función [displayMessage ()](https://github.com/mdn/learning-area/blob/master/javascript/building-blocks/functions/function-stage-4.html#L50) que creamos en el artículo anterior, no se devuelve ningún valor específico como resultado de la función que se invoca. Simplemente hace que aparezca un cuadro en algún lugar de la pantalla, ¡eso es todo! + +Generalmente, se usa un valor de retorno donde la función es un paso intermedio en un cálculo de algún tipo. Quieres llegar a un resultado final, que involucra algunos valores. Esos valores deben ser calculados por una función, que luego devuelve los resultados para que puedan usarse en la siguiente etapa del cálculo. + +### Using return values in your own functions + +To return a value from a custom function, you need to use ... wait for it ... the [return](/es/docs/Web/JavaScript/Reference/Statements/return) keyword. We saw this in action recently in our [random-canvas-circles.html](https://github.com/mdn/learning-area/blob/master/javascript/building-blocks/loops/random-canvas-circles.html) example. Our `draw()` function draws 100 random circles somewhere on an HTML {{htmlelement("canvas")}}: + +```js +function draw() { + ctx.clearRect(0,0,WIDTH,HEIGHT); + for (var i = 0; i < 100; i++) { + ctx.beginPath(); + ctx.fillStyle = 'rgba(255,0,0,0.5)'; + ctx.arc(random(WIDTH), random(HEIGHT), random(50), 0, 2 * Math.PI); + ctx.fill(); + } +} +``` + +Inside each loop iteration, three calls are made to the `random()` function, to generate a random value for the current circle's x coordinate, y coordinate, and radius, respectively. The `random()` function takes one parameter — a whole number — and it returns a whole random number between 0 and that number. It looks like this: + +```js +function randomNumber(number) { + return Math.floor(Math.random()*number); +} +``` + +This could be written as follows: + +```js +function randomNumber(number) { + var result = Math.floor(Math.random()*number); + return result; +} +``` + +But the first version is quicker to write, and more compact. + +We are returning the result of the calculation `Math.floor(Math.random()*number)` each time the function is called. This return value appears at the point the function was called, and the code continues. So for example, if we ran the following line: + +```js +ctx.arc(random(WIDTH), random(HEIGHT), random(50), 0, 2 * Math.PI); +``` + +and the three `random()` calls returned the values 500, 200, and 35, respectively, the line would actually be run as if it were this: + +```js +ctx.arc(500, 200, 35, 0, 2 * Math.PI); +``` + +The function calls on the line are run first and their return values substituted for the function calls, before the line itself is then executed. + +## Active learning: our own return value function + +Let's have a go at writing our own functions featuring return values. + +1. First of all, make a local copy of the [function-library.html](https://github.com/mdn/learning-area/blob/master/javascript/building-blocks/functions/function-library.html) file from GitHub. This is a simple HTML page containing a text {{htmlelement("input")}} field and a paragraph. There's also a {{htmlelement("script")}} element in which we have stored a reference to both HTML elements in two variables. This little page will allow you to enter a number into the text box, and display different numbers related to it in the paragraph below. +2. Let's add some useful functions to this `` del código fuente de nuestra versión final, el atributo `defer` especifica que el contenido del elemento {{htmlelement("script")}} no se ejecutará hasta que la página haya terminado de cargarse. + +## Almacenamiento de datos complejos — `IndexedDB` + +La {{web.link("/es/docs/Web/API/IndexedDB_API", "API IndexedDB")}} (a veces abreviada _IDB_) es un sistema de base de datos completo disponible en el navegador en el que puedes almacenar datos complejos relacionados, tipos de los cuales no se limitan a valores simples como cadenas o números. Puedes almacenar videos, imágenes y casi cualquier otra cosa en una instancia de `IndexedDB`. + +Sin embargo, esto tiene un costo: `IndexedDB` es mucho más complejo de usar que la API de almacenamiento web. En esta sección, solo vamos a arañar la superficie de lo que es capaz de hacer, pero te daremos lo suficiente para comenzar. + +### Trabajar con un ejemplo de almacenamiento de notas + +Aquí, mostraremos un ejemplo que te permite almacenar notas en tu navegador y verlas y eliminarlas cuando lo desees, lo cual te permitirá crearlo tú mismo y explicar las partes más fundamentales del _IDB_ a medida que avanzamos. + +La aplicación se parece a esta: + +![IDB en acción](https://mdn.mozillademos.org/files/15744/idb-demo.png) + +Cada nota tiene un título y un cuerpo de texto, cada uno editable individualmente. El código JavaScript que veremos a continuación tiene comentarios detallados para ayudarte a comprender lo que está sucediendo. + +### Primeros pasos + +1. En primer lugar, haz copias locales de nuestros archivos [`index.html`](https://github.com/mdn/learning-area/blob/master/javascript/apis/client-side-storage/indexeddb/notes/index.html), [`style.css`](https://github.com/mdn/learning-area/blob/master/javascript/apis/client-side-storage/indexeddb/notes/style.css) y [`index-start.js`](https://github.com/mdn/learning-area/blob/master/javascript/apis/client-side-storage/indexeddb/notes/index-start.js) en un nuevo directorio en tu máquina. +2. Échale un vistazo a los archivos. Verás que el HTML es bastante simple: un sitio web con encabezado y pie de página, así como un área de contenido principal que contiene un lugar para mostrar notas y un formulario para ingresar nuevas notas en la base de datos. El CSS proporciona un estilo simple para aclarar lo que está sucediendo. El archivo JavaScript contiene cinco constantes declaradas que contienen referencias al elemento {{htmlelement("ul")}} en el que se mostrarán las notas, el título y el cuerpo de elementos {{htmlelement("input")}}, el {{htmlelement("form")}} en sí mismo, y el {{htmlelement("button")}}. +3. Cambia el nombre de tu archivo JavaScript a `index.js`. Ahora estás listo para comenzar a agregarle código. + +### Configuración inicial de la base de datos + +Ahora veamos lo que tenemos que hacer en primer lugar, para configurar una base de datos. + +1. Debajo de las declaraciones constantes, agrega las siguientes líneas: + + ```js + // Crea una instancia de un objeto db para que almacenemos la base de datos abierta + let db; + ``` + + Aquí estamos declarando una variable llamada `db`, que luego se usará para almacenar un objeto que representa nuestra base de datos. Usaremos esto en algunos lugares, por lo que hemos declarado globalmente aquí para facilitar las cosas. + +2. A continuación, agrega lo siguiente al final de tu código: + + ```js + window.onload = function() { + + }; + ``` + + Escribiremos todo nuestro subsiguiente código dentro de esta función controladora de eventos `window.onload`, llamada cuando se activa el evento {{event("load")}} de la ventana, para asegurarnos de que no intentemos usar la funcionalidad `IndexedDB` antes de que la aplicación haya terminado de cargarse por completo (podría fallar si no lo hacemos). + +3. Dentro del controlador `window.onload`, agrega lo siguiente: + + ```js + // Abre nuestra base de datos; se crea si aún no existe + // (ve onupgradeneeded a continuación) + let request = window.indexedDB.open('notes_db', 1); + ``` + + Esta línea crea una `solicitud` para abrir la versión `1` de una base de datos llamada `notes_db`. Si esta aún no existe, se creará para ti mediante un código posterior. Verás que este patrón de solicitud se usa con mucha frecuencia en `IndexedDB`. Las operaciones de la base de datos llevan tiempo. No deseas colgar el navegador mientras esperas los resultados, por lo que las operaciones de la base de datos son {{Glossary("asíncronas")}}, lo que significa que en lugar de ocurrir de inmediato, sucederán en algún momento en el futuro, y recibirás una notificación cuando haya terminado. + + Para manejar esto en IndexedDB, crea un objeto de solicitud (que se puede llamar como desees; lo llamamos `request`, por lo que es obvio para qué sirve). Luego, usa controladores de eventos para ejecutar código cuando la solicitud se completa, falla, etc., que verás en uso a continuación. + + > **Nota:** El número de versión es importante. Si deseas actualizar tu base de datos (por ejemplo, cambiando la estructura de la tabla), debes ejecutar tu código nuevamente con un número de versión aumentado, un esquema diferente especificado dentro del controlador `onupgradeneeded` (ve más abajo), etc. No cubriremos la actualización de bases de datos en este sencillo tutorial. + +4. Ahora agrega los siguientes controladores de eventos justo debajo de tu adición anterior, nuevamente dentro del controlador `window.onload`: + + ```js + // un controlador de error significa que la base de datos no se abrió correctamente + request.onerror = function() { + console.log('No se pudo abrir la base de datos'); + }; + + // controlador onsuccess significa que la base de datos se abrió correctamente + request.onsuccess = function() { + console.log('Base de datos abierta con éxito'); + + // Almacena el objeto de base de datos abierto en la variable db. Esto se usa mucho a continuación + db = request.result; + + // Ejecute la función displayData() para mostrar las notas que ya están en la IDB + displayData(); + }; + ``` + + El controlador {{domxref("IDBRequest.onerror", "request.onerror")}} se ejecutará si el sistema dice que la solicitud falló. Esto te permite responder a este problema. En nuestro sencillo ejemplo, simplemente imprimimos un mensaje en la consola de JavaScript. + + El controlador {{domxref("IDBRequest.onsuccess", "request.onsuccess")}} por otro lado se ejecutará si la solicitud regresa con éxito, lo que significa que la base de datos se abrió correctamente. Si este es el caso, un objeto que representa la base de datos abierta pasa a estar disponible en la propiedad {{domxref("IDBRequest.result", "request.result")}}, lo que nos permite manipular la base de datos. Almacenamos esto en la variable `db` que creamos anteriormente para su uso posterior. También ejecutamos una función personalizada llamada `displayData()`, que muestra los datos en la base de datos dentro de {{HTMLElement("ul")}}. Lo ejecutamos ahora para que las notas que ya están en la base de datos se muestren tan pronto como se cargue la página. Verás esto definido más adelante. + +5. Finalmente, en esta sección, agregaremos probablemente el controlador de eventos más importante para configurar la base de datos: {{domxref("IDBOpenDBRequest.onupgradeneeded", "request.onupgradeneeded")}}. Este controlador se ejecuta si la base de datos aún no se ha configurado, o si la base de datos se abre con un número de versión mayor que la base de datos almacenada existente (al realizar una actualización). Agrega el siguiente código, debajo de tu controlador anterior: + + ```js + // Configura las tablas de la base de datos si esto aún no se ha hecho + request.onupgradeneeded = function(e) { + // Toma una referencia a la base de datos abierta + let db = e.target.result; + + // Crea un objectStore para almacenar nuestras notas (básicamente como una sola tabla) + // incluyendo una clave de incremento automático + let objectStore = db.createObjectStore('notes_os', {keyPath: 'id', autoIncrement: true}); + + // Define qué elementos de datos contendrá el objectStore + objectStore.createIndex('title', 'title', { unique: false }); + objectStore.createIndex('body', 'body', { unique: false }); + + console.log('Configuración de la base de datos completa'); + }; + ``` + + Aquí es donde definimos el esquema (estructura) de nuestra base de datos; es decir, el conjunto de columnas (o campos) que contiene. Aquí primero tomamos una referencia a la base de datos existente de la propiedad `result` del objetivo del evento (`e.target.result`), que es el objeto `request`. Esto es equivalente a la línea `db = request.result;` dentro del controlador `onsuccess`, pero aquí, debemos hacer esto por separado porque el controlador `onupgradeneeded` (si es necesario) se ejecutará antes que el controlador `onsuccess`, lo que significa que el valor `db` no estaría disponible si no hiciéramos esto. + + Luego usamos {{domxref("IDBDatabase.createObjectStore()")}} para crear un nuevo almacén de objetos dentro de nuestra base de datos abierta llamada `notes_os`. Esto es equivalente a una sola tabla en un sistema de base de datos convencional. Le hemos dado el nombre _notas_, y también hemos especificado un campo clave `autoIncrement` llamado `id` — en cada nuevo registro se le dará automáticamente un valor incrementado — el desarrollador no lo hace No es necesario establecer esto explícitamente. Al ser la clave, el campo `id` se utilizará para identificar registros de forma única, como cuando se elimina o muestra un registro. + + También creamos otros dos índices (campos) usando el método {{domxref("IDBObjectStore.createIndex()")}}: `title` (que contendrá un título para cada nota) y `body` (que contendrá el texto del cuerpo de la nota). + +Entonces, con este esquema de base de datos simple configurado, cuando comenzamos a agregar registros a la base de datos; cada uno se representará como un objeto siguiendo estas líneas: + +```js +{ + title: "Compra leche", + body: "Necesita leche de vaca y soja", + id: 8 +} +``` + +### Agregar datos a la base de datos + +Ahora veamos cómo podemos agregar registros a la base de datos. Esto se hará mediante el formulario de nuestra página. + +Debajo de tu controlador de eventos anterior (pero aún dentro del controlador `window.onload`), agrega la siguiente línea, que configura un controlador `onsubmit` que ejecuta una función llamada `addData()` cuando se envía el formulario (cuando se presiona el {{htmlelement("button")}} de enviar, lo que lleva a un envío exitoso del formulario): + +```js +// Crea un controlador onsubmit para que cuando se envíe el formulario se ejecute la función addData() +form.onsubmit = addData; +``` + +Ahora definamos la función `addData()`. Agrega esto debajo de tu línea anterior: + +```js +// Define la función addData() +function addData(e) { + // evitar el predeterminado: no queremos que el formulario se envíe de la forma convencional + e.preventDefault(); + + // toma los valores ingresados en los campos del formulario y los almacenar en un objeto listo para ser insertado en la base de datos + let newItem = { title: titleInput.value, body: bodyInput.value }; + + // abre una transacción de base de datos de lectura/escritura, lista para agregar los datos + let transaction = db.transaction(['notes_os'], 'readwrite'); + + // llama a un almacén de objetos que ya se ha agregado a la base de datos + let objectStore = transaction.objectStore('notes_os'); + + // Hacer una solicitud para agregar nuestro objeto newItem al almacén de objetos + let request = objectStore.add(newItem); + request.onsuccess = function() { + // Limpiar el formulario, listo para agregar la siguiente entrada + titleInput.value = ''; + bodyInput.value = ''; + }; + + // Informa sobre el éxito de la transacción completada, cuando todo esté hecho + transaction.oncomplete = function() { + console.log('Transacción completada: modificación de la base de datos finalizada.'); + + // actualiza la visualización de datos para mostrar el elemento recién agregado, ejecutando displayData() nuevamente. + displayData(); + }; + + transaction.onerror = function() { + console.log('Transacción no abierta debido a error'); + }; +} +``` + +Esto es bastante complejo; desglosándolo, podemos: + +- Ejecuta {{domxref("Event.preventDefault()")}} en el objeto de evento para detener el envío del formulario de la manera convencional (esto provocaría una actualización de la página y estropearía la experiencia). +- Crea un objeto que represente un registro para ingresar a la base de datos, llenándolo con valores de las entradas del formulario. ten en cuenta que no tenemos que incluir explícitamente un valor de `id`; como explicamos anteriormente, esto se completa automáticamente. +- Abre una transacción `readwrite` contra el almacén de objetos `notes_os` utilizando el método {{domxref("IDBDatabase.transaction()")}}. Este objeto de transacción nos permite acceder al almacén de objetos para que podamos hacerle algo, p. ej. agregar un nuevo registro. +- Accede a la tienda de objetos utilizando el método {{domxref("IDBTransaction.objectStore()")}}, guardando el resultado en la variable `objectStore`. +- Agrega el nuevo registro a la base de datos usando {{domxref("IDBObjectStore.add()")}}. Esto crea un objeto `request`, de la misma manera que hemos visto antes. +- Agrega un grupo de controladores de eventos a `request` y a `transaction` para ejecutar código en puntos críticos del ciclo de vida. Una vez que la solicitud ha tenido éxito, borramos las entradas del formulario y estamos listos para ingresar la siguiente nota. Una vez que la transacción se ha completado, ejecutamos la función `displayData()` nuevamente para actualizar la visualización de notas en la página. + +### Visualización de los datos + +Ya hemos hecho referencia a `displayData()` dos veces en nuestro código, por lo que probablemente sea mejor definirla. Agrega esto a tu código, debajo de la definición de función anterior: + +```js +// Define la función displayData() +function displayData() { + // Aquí vaciamos el contenido del elemento de la lista cada vez que se actualiza la pantalla + // Si no hiciste esto, obtendrás duplicados en la lista cada vez que se agregue una nueva nota + while (list.firstChild) { + list.removeChild(list.firstChild); + } + + // Abre el almacén de objetos y luego obtiene un cursor, que recorre todos los + // diferentes elementos de datos en el almacén + let objectStore = db.transaction('notes_os').objectStore('notes_os'); + objectStore.openCursor().onsuccess = function(e) { + // Obtiene una referencia al cursor + let cursor = e.target.result; + + // Si todavía hay otro elemento de datos para iterar, sigue ejecutando este código + if(cursor) { + // Crea un elemento de lista, h3 y p para poner cada elemento de datos dentro al mostrarlo + // estructura el fragmento HTML y lo anexa dentro de la lista + const listItem = document.createElement('li'); + const h3 = document.createElement('h3'); + const para = document.createElement('p'); + + listItem.appendChild(h3); + listItem.appendChild(para); + list.appendChild(listItem); + + // Coloca los datos del cursor dentro de h3 y para + h3.textContent = cursor.value.title; + para.textContent = cursor.value.body; + + // Almacena el ID del elemento de datos dentro de un atributo en listItem, para que sepamos + // a qué elemento corresponde. Esto será útil más adelante cuando queramos eliminar elementos. + listItem.setAttribute('data-note-id', cursor.value.id); + + // Crea un botón y lo coloca dentro de cada listItem + const deleteBtn = document.createElement('button'); + listItem.appendChild(deleteBtn); + deleteBtn.textContent = 'Delete'; + + // Establece un controlador de eventos para que cuando se hace clic en el botón, el elemento deleteItem() + // se ejecuta la función + deleteBtn.onclick = deleteItem; + + // Iterar al siguiente elemento del cursor + cursor.continue(); + } else { + // Nuevamente, si el elemento de la lista está vacío, muestra el mensaje 'No hay notas almacenadas' + if(!list.firstChild) { + const listItem = document.createElement('li'); + listItem.textContent = 'No hay notas almacenadas.'; + list.appendChild(listItem); + } + // si no hay más elementos de cursor para iterar, dilo + console.log('Se muestran todas las notas'); + } + }; +} +``` + +De nuevo, analicemos esto: + +- Primero vaciamos el contenido del elemento {{htmlelement("ul")}}, antes de llenarlo con el contenido actualizado. Si no hiciera esto, terminarías con una enorme lista de contenido duplicado que se agrega con cada actualización. +- A continuación, obtenemos una referencia al almacén de objetos `notes_os` usando {{domxref("IDBDatabase.transaction()")}} y {{domxref("IDBTransaction.objectStore()")}} como hicimos en `addData()`, excepto que aquí los estamos encadenando juntos en una línea. +- El siguiente paso es usar el método {{domxref("IDBObjectStore.openCursor()")}} para abrir una solicitud de un cursor; esta es una construcción que se puede usar para iterar sobre los registros en un almacén de objetos. Encadenamos un controlador `onsuccess` al final de esta línea para que el código sea más conciso: cuando el cursor se devuelve correctamente, se ejecuta el controlador. +- Obtenemos una referencia al propio cursor (un objeto {{domxref("IDBCursor")}}) usando let `cursor = e.target.result`. +- A continuación, verificamos si el cursor contiene un registro del almacén de datos (`if (cursor) {...}`); si es así, creamos un fragmento DOM, lo llenamos con los datos del registro y lo insertamos en la página (dentro del elemento `