Skip to content

Commit ddd1562

Browse files
joaquineliovrivas
andauthored
Apply megasuggestions
Co-authored-by: Víctor Manuel Rivas Santos <[email protected]>
1 parent 84de156 commit ddd1562

File tree

1 file changed

+28
-28
lines changed

1 file changed

+28
-28
lines changed

6-data-storage/03-indexeddb/article.md

Lines changed: 28 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ Técnicamente, los datos son almacenados bajo el directorio raíz del usuario ju
2424
Navegadores y usuarios diferentes tendrán cada uno su propio almacenamiento independiente.
2525
```
2626

27-
## Abrir una base de datos, "open"
27+
## Apertura de una base de datos, "open"
2828

2929
Para empezar a trabajar con IndexedDB, primero necesitamos conectarnos o "abrir" (`open`) una base de datos.
3030

@@ -37,7 +37,7 @@ let openRequest = indexedDB.open(name, version);
3737
- `name` -- un string, el nombre de la base de datos.
3838
- `version` -- un entero positivo, predeterminado en `1` (explicado más abajo).
3939

40-
Podemos tener muchas bases de datos con nombres diferentes, pero todas deben existir dentro del mismo origen (dominio/protocolo/puerto). Un sitio web no puede acceder bases de datos de otro.
40+
Podemos tener muchas bases de datos con nombres diferentes, pero todas ellas existen dentro del mismo origen (dominio/protocolo/puerto). Un sitio web no puede acceder bases de datos de otro.
4141

4242
La llamada devuelve un objeto `openRequest`, debemos escuchar en él los eventos:
4343
- `success`: la base de datos está lista. Hay un "objeto database" en `openRequest.result` que habremos de usar en las llamadas subsiguientes.
@@ -127,7 +127,7 @@ Entonces hay una primera pestaña con una conexión abierta a una base con versi
127127

128128
El problema es que la misma base está compartida entre las dos pestañas, por ser del mismo sitio y origen. Y no puede ser versión `1` y `2` al mismo tiempo. Para ejecutar la actualización a la versión `2`, todas las conexiones a la versión 1 deben ser cerradas, incluyendo las de la primera pestaña.
129129

130-
Para organizar esto, se dispara el evento `versionchange` (cambio-de-versión) en el objeto de base de datos. Debemos escucharlo y cerrar la conexión vieja (y probablemente sugerir una recarga de página, para cargar el código actualizado).
130+
Para detectar estas situaciones, se dispara automáticamente el evento `versionchange` (cambio-de-versión) en el objeto de base de datos. Debemos escuchar dicho evento y cerrar la conexión vieja (y probablemente sugerir una recarga de página, para cargar el código actualizado).
131131

132132
Si no escuchamos el evento `versionchange` y no cerramos la conexión vieja, entonces la segunda y más nueva no se podrá hacer. El objeto `openRequest` emitirá el evento `blocked` en lugar de `success`. Entonces la segunda pestaña no funcionará.
133133

@@ -171,21 +171,21 @@ Podemos manejar las cosas más suavemente en `db.onversionchange`, como pedirle
171171

172172
Como alternativa podríamos no cerrar la base en `db.onversionchange` sino usar `onblocked` de la nueva pestaña para advertirle que no puede crear una nueva conexión hasta que cierre las viejas.
173173

174-
Estas colisiones ocurren raramente, pero debemos al menos tener algún manejo de ella, como mínimo un manejador `onblocked` para evitar que nuestro script muera silenciosamente.
174+
Estas colisiones ocurren raramente, pero deberíamos tener algún manejo de ella, como mínimo un manejador `onblocked` para evitar que nuestro script muera silenciosamente.
175175

176176
## Almacén de objetos, "store"
177177

178178
Para almacenar algo en IndexedDB, necesitamos un "almacén de objetos" *object store*.
179179

180-
Un almacén de objetos es un concepto central de IndexedDB. Contrapartes en otras bases de datos tienen "tablas" o "colecciones". Es donde los datos son almacenados. Una base de datos puede tener múltiples almacenes: una para usuarios, otra para bienes, etc.
180+
Un almacén de objetos es un concepto central de IndexedDB. Equivale a lo que en otras bases de datos se denominan "tablas" o "colecciones". Es donde los datos son almacenados. Una base de datos puede tener múltiples almacenes: uno para usuarios, otro para bienes, etc.
181181

182182
A pesar de llamarse "almacén de objetos", también puede almacenar tipos primitivos.
183183

184184
**Podemos almacenar casi cualquier valor, incluyendo objetos complejos.**
185185

186186
IndexedDB usa el [algoritmo de serialización estándar](https://www.w3.org/TR/html53/infrastructure.html#section-structuredserializeforstorage) para clonar-y-almacenar un objeto. Es como `JSON.stringify` pero más poderoso, capaz de almacenar muchos tipos de datos más.
187187

188-
Hay objetos que no puede ser almacenados, por ejemplo los que tienen referencias circulares. Tales objetos no son serializables. `JSON.stringify` también falla con ellos.
188+
Hay objetos que no pueden ser almacenados, por ejemplo los que tienen referencias circulares. Tales objetos no son serializables. `JSON.stringify` también falla con ellos.
189189

190190
**Debe haber una clave `key` única para cada valor del almacén.**
191191

@@ -208,7 +208,7 @@ Ten en cuenta que esta operación es sincrónica, no requiere `await`.
208208

209209
- `name` es el nombre del almacén, por ejemplo `"books"`,
210210
- `keyOptions` es un objeto opcional con una de estas dos propiedades:
211-
- `keyPath` -- la ruta a un propiedad de objeto que IndexedDB usará como clave, por ejemplo `id`.
211+
- `keyPath` -- la ruta a una propiedad del objeto que IndexedDB usará como clave, por ejemplo `id`.
212212
- `autoIncrement` -- si es `true`, la clave para el objeto nuevo que se almacene se generará automáticamente con un número autoincremental.
213213

214214
Si no establecemos `keyOptions`, necesitaremos proporcionar una clave explícitamente más tarde: al momento de almacenar un objeto.
@@ -218,7 +218,7 @@ Por ejemplo, este objeto usa la propiedad `id` como clave:
218218
db.createObjectStore('books', {keyPath: 'id'});
219219
```
220220

221-
**Un almacén de objetos solo puede ser creado o modificado durante la actualización de su versión. Esto es en el manejador `upgradeneeded`.**
221+
**Un almacén de objetos solo puede ser creado o modificado durante la actualización de su versión, esto es, en el manejador `upgradeneeded`.**
222222

223223
Esto es una limitación técnica. Fuera del manejador podremos agregar/borrar/modificar los datos, pero los almacenes de objetos solo pueden ser creados/borrados/alterados durante la actualización de versión.
224224

@@ -276,15 +276,15 @@ db.transaction(store[, type]);
276276
- `readonly` -- solo puede leer (es el predeterminado).
277277
- `readwrite` -- puede leer o escribir datos (pero no crear/quitar/alterar almacenes de objetos).
278278

279-
También existe el tipo de transacción `versionchange`: tal transacción puede hacer de todo, pero no podemos crearla nosotros a mano. IndexedDB la crea automáticamente cuando abre la base de datos para el manejador `updateneeded`. Por ello es el único lugar donde podemos actualizar la estructura de base de datos, crear o quitar almacenes de objetos.
279+
También existe el tipo de transacción `versionchange`: tal transacción puede hacer de todo, pero no podemos crearla nosotros a mano. IndexedDB la crea automáticamente cuando abre la base de datos para el manejador `updateneeded`. Por ello, es el único lugar donde podemos actualizar la estructura de base de datos, crear o quitar almacenes de objetos.
280280

281281
```smart header="¿Por qué hay diferentes tipos de transacciones?"
282282
El rendimiento es la razón por la que necesitamos identificar las transacciones como `readonly` (lectura solamente) o `readwrite` (lectura y escritura).
283283
284284
Muchas transacciones `readonly` pueden leer en un mismo almacén concurrentemente, en cambio las transacciones de escritura `readwrite`, no. Una transacción `readwrite` bloquea el almacén para escribir en él. La siguiente transacción debe esperar a que la anterior termine antes de acceder al mismo almacén.
285285
```
286286

287-
Una vez que la transacción es creada, podemos agregar un ítem al almacén:
287+
Una vez que la transacción ha sido creada, podemos agregar un ítem al almacén:
288288

289289
```js
290290
let transaction = db.transaction("books", "readwrite"); // (1)
@@ -378,7 +378,7 @@ En el ejemplo de arriba podemos hacer una nueva `db.transaction` justo antes de
378378

379379
Pero, si queremos mantener las operaciones juntas en una transacción, será mucho mejor separar las transacciones IndexedDB de la parte asincrónica.
380380

381-
Primero, hacer `fetch` y preparar todos los datos que fueran necesarios, y solo entonces crear una transacción y ejecutar todas las peticiones de base de datos. Así, funcionaría.
381+
Primero, hacer `fetch` y preparar todos los datos que fueran necesarios y, solo entonces, crear una transacción y ejecutar todas las peticiones de base de datos. Así, funcionaría.
382382

383383
Para detectar el momento de finalización exitosa, podemos escuchar al evento `transaction.oncomplete`:
384384

@@ -411,7 +411,7 @@ Esto es esperable, no solo por posibles errores de nuestro lado, sino también p
411411

412412
**Una petición fallida automáticamente aborta la transacción, cancelando todos sus cambios.**
413413

414-
En algunas situaciones, podemos querer manejar el fallo (por ejemplo, intentar otra petición), sin cancelar los cambios en curso, y continuar la transacción. Eso es posible. El manejador `request.onerror` está habilitado a evitar el aborto de la transacción llamando a `event.preventDefault()`.
414+
En algunas situaciones, podemos querer manejar el fallo (por ejemplo, intentar otra petición) sin cancelar los cambios en curso, y continuar la transacción. Eso es posible. El manejador `request.onerror` es capaz de evitar el aborto de la transacción llamando a `event.preventDefault()`.
415415

416416
En el ejemplo que sigue, un libro nuevo es agregado con la misma clave (`id`) que otro existente. El método `store.add` genera un `"ConstraintError"` en ese caso. Lo manejamos sin cancelar la transacción:
417417

@@ -529,7 +529,7 @@ books.getAllKeys(IDBKeyRange.lowerBound('js', true))
529529
```smart header="El almacén de objetos siempre está ordenado"
530530
El almacén internamente guarda los valores ordenados por clave.
531531
532-
Entonces, las peticiones que que devuelvan muchos valores siempre serán devueltos ordenados por clave.
532+
Entonces, en las peticiones que devuelvan varios valores, estos siempre estarán ordenados por la clave.
533533
```
534534

535535
## Buscando por cualquier campo con un índice
@@ -554,7 +554,7 @@ En nuestro ejemplo, almacenamos libros usando la propiedad `id` como clave.
554554

555555
Digamos que queremos buscar por precio `price`.
556556

557-
Primero necesitamos crear un índice. Esto debe hacerse en `upgradeneeded`, igual que ObjectStore.
557+
Primero necesitamos crear un índice. Esto debe hacerse en `upgradeneeded`, al igual que hacíamos la creación del almacén de objetos.
558558

559559
```js
560560
openRequest.onupgradeneeded = function() {
@@ -619,7 +619,7 @@ Por ejemplo:
619619
books.delete('js');
620620
```
621621

622-
Si queremos borrar libros basados en un precio u otro campo del objeto, debemos primero encontrar la clave en el índice, luego llamar a `delete` con él:
622+
Si queremos borrar libros basados en un precio u otro campo del objeto, debemos primero encontrar la clave en el índice, luego llamar a `delete` con dicha clave:
623623

624624
```js
625625
// encuentra la clave donde price = 5
@@ -644,9 +644,9 @@ Pero un almacén de objetos puede ser enorme, incluso más que la memoria dispon
644644

645645
¿Qué hacer?
646646

647-
Los cursores brindan los medios para trabajar con eso.
647+
Los cursores brindan los medios para manejar esta situación.
648648

649-
**Un *cursor* es un objeto especial que, dada una consulta, recorre el almacén y devuelve un par clave/valor a la vez, así ahorrando memoria.**
649+
**Un *cursor* es un objeto especial que, dada una consulta, recorre el almacén y devuelve un solo par clave/valor cada vez, ahorrando así memoria.**
650650

651651
Como un almacén está ordenado internamente por clave, un cursor lo recorre en el orden de la clave (ascendende de forma predeterminada).
652652

@@ -662,7 +662,7 @@ let request = store.openCursor(query, [direction]);
662662
- **`direction`** es un argumento opcional, el orden que se va a usar:
663663
- `"next"` -- el predeterminado: el cursor recorre en orden ascendente comenzando por la clave más baja.
664664
- `"prev"` -- el orden inverso: decrece comenzando con el registro con la clave más alta.
665-
- `"nextunique"`, `"prevunique"` -- igual que las anteriores, pero saltando los registros con la misma clave (válido solo para cursores sobre índices. Por ejemplo, de múltiples libros con price=5, solamente el primero será devuelto).
665+
- `"nextunique"`, `"prevunique"` -- igual que las anteriores, pero saltando los registros con la misma clave (válido solo para cursores sobre índices; por ejemplo, de múltiples libros con price=5, solamente el primero será devuelto).
666666

667667
**La diferencia principal del cursor es que `request.onsuccess` se dispara múltiples veces: una por cada resultado.**
668668

@@ -693,11 +693,11 @@ Los principales métodos de cursor son:
693693
- `advance(count)` -- avanza el cursor `count` veces, saltando valores.
694694
- `continue([key])` -- avanza el cursor al siguiente valor en el rango o, si se provee la clave `key`, al valor inmediatamente posterior a `key`.
695695

696-
El evento `onsuccess` será llamado haya o no más valores coincidentes, y en `result` obtenemos el cursor apuntando al siguiente registro, o `undefined`.
696+
El evento `onsuccess` será llamado haya o no más valores coincidentes, y en `result` obtenemos el cursor apuntando al siguiente registro o `undefined`.
697697

698-
En el ejemplo anterior el cursor fue hecho sobre el almacén de objetos.
698+
En el ejemplo anterior, el cursor fue hecho sobre el almacén de objetos.
699699

700-
Pero también podemos hacerlo sobre un índice. Recordamos, los índices nos permiten buscar por los campos del objeto. Los cursores sobre índices hacen precisamente lo mismo que sobre el almacén de objetos: Ahorran memoria al devolver una valor a la vez.
700+
Pero también podemos hacerlo sobre un índice. Recordamos, los índices nos permiten buscar por los campos del objeto. Los cursores sobre índices hacen precisamente lo mismo que sobre el almacén de objetos: ahorran memoria al devolver un solo valor cada vez.
701701

702702
Para cursores sobre índices, `cursor.key` es la clave del índice (es decir "price"), y debemos usar la propiedad `cursor.primaryKey` para la clave del objeto:
703703

@@ -721,9 +721,9 @@ request.onsuccess = function() {
721721

722722
## Contenedor promisificador
723723

724-
Agregar `onsuccess/onerror` a cada petición es una tarea agobiante. A veces podemos hacernos la vida más fácil usando delegación de eventos, es decir establecer manejadores para las transacciones completas, pero `async/await` es mucho más conveniente.
724+
Agregar `onsuccess/onerror` a cada petición es una tarea agobiante. A veces podemos hacernos la vida más fácil usando delegación de eventos (por ejemplo, estableciendo manejadores para las transacciones completas), pero `async/await` es mucho más conveniente.
725725

726-
Usemos en adelante para este capítulo un contenedor (wrapper) liviano que añade promesas <https://github.com/jakearchibald/idb>. Este crea un objeto global `idb` con métodos IndexedDB [promisificados](info:promisify).
726+
Usemos en adelante para este capítulo un contenedor (wrapper) liviano que añade promesas <https://github.com/jakearchibald/idb>. Este contenedor crea un objeto global `idb` con métodos IndexedDB [promisificados](info:promisify).
727727

728728
Entonces, en lugar de `onsuccess/onerror`, podemos escribir:
729729

@@ -755,7 +755,7 @@ Así tenemos todo lo dulce de "código async plano" y "try..catch".
755755

756756
### Manejo de Error
757757

758-
Si no atrapamos un error, este se propaga hasta el más `try..catch` externo más cercano.
758+
Si no atrapamos un error, este se propaga hasta el `try..catch` externo más cercano.
759759

760760
Un error no atrapado se vuelve un evento "rechazo de promesa no manejado" sobre el objeto `window`.
761761

@@ -772,7 +772,7 @@ window.addEventListener('unhandledrejection', event => {
772772
### La trampa "transacción inactiva"
773773

774774

775-
Como sabemos, una transacción se autofinaliza tan pronto como el navegador termina el código actual y las microtareas. Entonces si ponemos una *macrotarea* como `fetch` en el medio de una transacción, la transacción no esperará a que termine. Simplemente se autofinaliza. Así la siguiente petición fallaría.
775+
Como sabemos, una transacción se autofinaliza tan pronto como el navegador termina el código actual y las microtareas. Por tanto, si ponemos una *macrotarea* como `fetch` en el medio de una transacción, la transacción no esperará a que termine. Simplemente se autofinaliza. Así la siguiente petición fallaría.
776776

777777

778778
Para el contenedor de promisificación y `async/await` la situación es la misma.
@@ -790,7 +790,7 @@ await fetch(...); // (*)
790790
await inventory.add({ id: 'js', price: 10, created: new Date() }); // Error
791791
```
792792

793-
El `inventory.add` que sigue a `fetch` `(*)` falla con el error "transacción inactiva", porque la transacción se autocompletó y para ese momento ya está cerrada.
793+
El `inventory.add` que sigue a `fetch` `(*)` falla con el error "transacción inactiva", porque la transacción se autocompletó y, llegado ese momento, ya está cerrada.
794794

795795
La forma de sortear esto es la misma que con el IndexedDB nativo: Hacer una nueva transacción o simplemente partir las cosas.
796796
1. Preparar los datos y buscar todo lo que sea necesario primero.
@@ -805,7 +805,7 @@ Esto funciona bien la mayor parte del tiempo. Los ejemplos están en la página
805805
En algunos raros casos necesitamos el objeto `request` original. Podemos accederlo con la propiedad `promise.request` de la promesa:
806806

807807
```js
808-
let promise = books.add(book); // obtener una promesa (no espera por su resultado)
808+
let promise = books.add(book); // obtiene una promesa (no espera por su resultado)
809809

810810
let request = promise.request; // objeto request nativo
811811
let transaction = request.transaction; // objeto transaction nativo
@@ -817,7 +817,7 @@ let result = await promise; // si aún se necesita
817817

818818
## Resumen
819819

820-
IndexedDB puede ser pensado como "localStorage con esteroides". Es una simple base de datos de clave-valor, suficientemente poderosa para apps fuera de línea y fácil de usar.
820+
IndexedDB puede considerarse como "localStorage con esteroides". Es una simple base de datos de clave-valor, suficientemente poderosa para apps fuera de línea y fácil de usar.
821821

822822
El mejor manual es la especificación, [la actual](https://www.w3.org/TR/IndexedDB-2/) es 2.0, pero algunos métodos de [3.0](https://w3c.github.io/IndexedDB/) (no muy diferente) están soportados parcialmente.
823823

0 commit comments

Comments
 (0)