Símbolos de JavaScript, iteradores, generadores, asincrónicos / esperantes e iteradores asincrónicos: todo explicado simplemente

Resultado de imagen para logo facebook vector

Resultado de imagen para web logo pngResultado de imagen para logo youtube png

 

Algunas características de JavaScript (ECMAScript) son más fáciles de entender que otras. Generatorsse ven raros, como punteros en C / C ++. Symbolslogran parecerse tanto a objetos primitivos como a objetos al mismo tiempo.

Estas características están interrelacionadas y se complementan entre sí. Entonces no puedes entender una cosa sin entender la otra.

Así que en este artículo, voy a cubrir symbolsglobal symbolsiteratorsiterablesgenerators , async/await async iteratorsExplicaré ” por qué ” están allí en primer lugar y también mostraré cómo funcionan con algunos ejemplos útiles.

Este es un tema relativamente avanzado, pero no es ciencia espacial. Este artículo debería darte una muy buena comprensión de todos estos conceptos.

OK, comencemos.🚀

Símbolos

En ES2015, se creó un nuevo (6º) tipo de datos llamado symbol.

¿POR QUÉ?

Las tres razones principales fueron:

Motivo n. ° 1: agregar nuevas funciones principales con compatibilidad hacia atrás

Los desarrolladores de JavaScript y el comité ECMAScript ( TC39 ) necesitaban una forma de agregar nuevas propiedades de objeto sin romper los métodos existentes como for inbucles o métodos de JavaScript como Object.keys.

Por ejemplo, si tengo un objeto, var myObject = {firstName:'raja', lastName:'rao'} y si lo ejecuto Object.keys(myObject), volvería [firstName, lastName] .

Ahora bien, si añadimos otra propiedad, digamos newPropertymyObject , y si se ejecuta Object.keys(myObject)se debe todavía volver los viejos valores (esto es, de alguna manera hacer que ignoran el recién agregado newproperty), y mostrar simplemente [firstName, lastName] - no [firstName, lastName, newProperty] . ¿Como hacer eso?

Realmente no podríamos hacer esto antes, por lo que Symbolsse creó un nuevo tipo de datos llamado .

Si agrega newPropertycomo un símbolo, entonces Object.keys(myObject)ignoraría esto (ya que no lo conoce), ¡y aún así regresará [firstName, lastName] !

Razón # 2 – Evite las colisiones de nombres

También querían mantener estas propiedades únicas. De esta forma, pueden seguir agregando nuevas propiedades (y puede agregar propiedades de objetos) a globales sin preocuparse por las colisiones de nombres.

Por ejemplo, supongamos que tiene un objeto donde agrega una personalizada toUpperCasea global Array.prototype .

Ahora, imagina que cargaste otra biblioteca (o salió ES2019) y tenía una versión diferente de Array.prototype.toUpperCase.Entonces tu función podría romperse debido a la colisión del nombre.

Entonces, ¿cómo resuelves esta colisión del nombre que tal vez no conozcas? Ahí es donde Symbolsentran. Crean internamente valores únicos que le permiten crear propiedades de adición sin preocuparse por la colisión de nombres.

Motivo n. ° 3: permitir el acceso a los métodos básicos a través de símbolos “conocidos”

Supongamos que quiere alguna función central, por ejemplo, String.prototype.searchllamar a su función personalizada. Es decir, ¡ ‘somestring’.search(myObject);debería llamar a la myObject’sfunción de búsqueda y pasarla ‘somestring’ como parámetro! ¿Como hacemos eso?

Aquí es donde ES2015 creó un conjunto de símbolos globales denominados símbolos “conocidos”. Y mientras su objeto tenga uno de esos símbolos como propiedad, ¡puede redirigir las funciones centrales para llamar a su función!

No podemos hablar mucho sobre esto ahora, así que entraré en detalles un poco más adelante en este artículo. Pero primero, aprendamos cómo funcionan realmente los Símbolos.

Creando Símbolos

Puede crear un símbolo llamando a una función / objeto global llamado Symbol . Esa función devuelve un valor de tipo de datos symbol.

Nota: Los símbolos pueden aparecer como Objetos porque tienen métodos, pero no son, son primitivos. Puedes pensar en ellos como objetos “especiales” que tienen algunas similitudes con objetos regulares, pero que no se comportan como objetos regulares.

Por ejemplo: los símbolos tienen métodos como Objetos, pero a diferencia de los objetos son inmutables y únicos.

Los símbolos no se pueden crear con la palabra clave “nueva”

Debido a que los símbolos no son objetos y newse supone que la palabra clave devuelve un Objeto, no podemos usar newpara devolver un symbols tipo de datos.

var mySymbol = new Symbol (); // lanza un error

Los símbolos tienen “descripción”

Los símbolos pueden tener una descripción, solo para fines de registro.

// la variable mySymbol ahora tiene un valor único de "símbolo" 
// su descripción es "algún texto" 
const mySymbol = Symbol ('some text');

Los símbolos son únicos

const mySymbol1 = Symbol ('algún texto'); 
const mySymbol2 = Symbol ('algún texto'); 
mySymbol1 == mySymbol2 // falso

Los símbolos se comportan como un singleton si usamos el método “Symbol.for”

En lugar de crear una symbolvía Symbol() , puede crearla a través de Symbol.for(<key>). Esto toma una “clave” (cadena) para crear un Símbolo. Y si keyya existe un símbolo , ¡simplemente devuelve el viejo símbolo! Entonces, se comporta como un singleton si usamos el Symbol.formétodo.

var = mySymbol1 Símbolo .para ( 'algunos de los principales'); // crea un nuevo símbolo 
mySymbol2 var = Símbolo .para ( 'algunos de los principales'); // ** devuelve el mismo símbolo
 mySymbol1 == mySymbol2 // true

La verdadera razón para usar el  .fores crear un Símbolo en un lugar y acceder al mismo Símbolo desde otro lugar.

Precaución: Symbol.for hará que el símbolo no sea único en el sentido de que terminará anulando los valores si las claves son las mismas. ¡Intenta evitar esto si es posible!

La “descripción” de Symbol versus “clave”

Solo para aclarar las cosas, si no las usa Symbol.for , los Símbolos son únicos. Sin embargo, si lo usa, entonces si key no es único, los símbolos devueltos tampoco serán únicos.

Los símbolos pueden ser una clave de propiedad del objeto

Esto es algo muy único sobre Símbolos, y también lo más confuso. Aunque parecen un objeto, son primitivos. Y podemos adjuntar un símbolo a un Objeto como una clave de propiedad como una Cadena.

De hecho, esta es una de las formas principales de usar símbolos, ¡como propiedades de objetos!

Nota: Las propiedades del objeto que son símbolos se conocen como “propiedades con clave”.

Brackets operador vs. operador punto

No puede usar un operador de punto porque los operadores de punto solo trabajan en propiedades de cadena, por lo que debe usar un operador de corchetes.

3 razones principales para usar Símbolos: una revisión

Revisemos las tres razones principales ahora que sabemos cómo funcionan los Símbolos.

Razón # 1 – Los símbolos son invisibles para los bucles y otros métodos

El bucle for-in en el siguiente ejemplo gira sobre un objeto objpero no lo sabe (o lo ignora) prop3prop4porque son símbolos.

A continuación se muestra otro ejemplo donde Object.keysObject.getOwnPropertyNamesestán ignorando los nombres de propiedades que son símbolos.

Razón # 2 – Los símbolos son únicos

Supongamos que quiere una característica llamada Array.prototype.includesen el Arrayobjeto global . includesChocará con el método predeterminado que JavaScript (ES2018) viene con la opción predeterminada . ¿Cómo lo agregas sin colisionar?

Primero, cree una variable con el nombre propio includesy asígnele un símbolo. A continuación, agregue esta variable (ahora un símbolo) a la Arraynotación global de corchetes. Asigna cualquier función que desees.

Finalmente llame a esa función usando la notación de corchetes. Pero tenga en cuenta que debe pasar el símbolo real entre corchetes como: arr[includes]()y no como una cadena.

Motivo n. ° 3 Símbolos conocidos (es decir, símbolos “globales”)

De manera predeterminada, JavaScript crea automáticamente un conjunto de variables de símbolos y las asigna al Symbolobjeto global (sí, lo mismo Symbol()que usamos para crear símbolos).

En ECMAScript 2015, se añaden entonces estos símbolos para los métodos básicos, tales como String.prototype.searchString.prototype.replacede los objetos básicos, tales como matrices y cadenas.

Algunos ejemplos de estos símbolos son: Symbol.matchSymbol.replaceSymbol.searchSymbol.iteratorSymbol.split.

Dado que estos símbolos globales son globales y están expuestos, podemos hacer que los métodos centrales invoquen nuestras funciones personalizadas en lugar de las internas.

Un ejemplo: Symbol.search

Por ejemplo, el String.prototype.searchmétodo público del objeto String busca un regExp o una cadena y devuelve el índice si se encuentra.

En ES2015, primero comprueba si el Symbol.searchmétodo se implementa en la consulta regExp (objeto RegExp). Si es así, llama a esa función y delega el trabajo en eso. Y los objetos centrales como RegExp implementan el Symbol.searchsímbolo que realmente hace el trabajo.

Funcionamiento interno de Symbol.search (COMPORTAMIENTO POR DEFECTO)

  1. Analizar gramaticalmente ‘rajarao’.search(‘rao’);
  2. Convierte “rajarao” en String Object new String(“rajarao”)
  3. Convierte “rao” en objeto RegExp new Regexp(“rao”)
  4. searchMétodo de llamada del objeto String “rajarao”.
  5. searchmétodo internamente llama al Symbol.searchmétodo en el objeto “rao” (delega la búsqueda al objeto “rao”) y pasa el “rajarao”. Algo como esto:"rao"[Symbol.search]("rajarao")
  6. "rao"[Symbol.search]("rajarao")devuelve el resultado del índice en cuanto 4a la searchfunción y, finalmente, searchregresa 4a nuestro código.

El fragmento de pseudo código siguiente muestra cómo funciona el código internamente:

Pero la belleza es que ya no tienes que haber aprobado RegExp. Puede pasar cualquier objeto personalizado que implemente Symbol.searchy devolver lo que quiera y esto continuará funcionando.

Vamos a ver.

Personalizar el método String.search para llamar a nuestra función

El siguiente ejemplo muestra cómo podemos String.prototype.searchllamar a Productla función de búsqueda de nuestra clase, gracias a Symbol.searchglobal Symbol.

Funcionamiento interno de Symbol.search (COMPORTAMIENTO PERSONALIZADO)

  1. Analizar gramaticalmente ‘barsoap’.search(soapObj);
  2. Convierte “barsoap” en String Object new String(“barsoap”)
  3. Como soapObjya es un objeto, no hagas ninguna conversión
  4. searchMétodo de llamada del objeto Cadena “barsoap”.
  5. searchel método llama internamente el Symbol.searchmétodo al ” soapObj” objeto (es decir, delega la búsqueda al ” soapObj” objeto) y pasa el “barsoap”. Algo como esto:soapObj[Symbol.search]("barsoap")
  6. soapObj[Symbol.search]("barsoap")devuelve el resultado del índice FOUNDpara searchfuncionar y, finalmente, searchregresa FOUNDa nuestro código.

Esperemos que tenga una buena comprensión de Símbolos ahora.

OK, pasemos a Iteradores.

Iteradores e Iterables

¿POR QUÉ?

En casi todas nuestras aplicaciones, estamos lidiando constantemente con listas de datos y tenemos que mostrar esos datos en el navegador o la aplicación móvil. Por lo general, escribimos nuestros propios métodos para almacenar y extraer esos datos.

Pero la cuestión es que ya tenemos métodos estándar como el for-ofoperador de bucle y spread ( ) para extraer colecciones de datos de objetos estándar como matrices, cadenas y mapas. ¿Por qué no podemos usar estos métodos estándar para nuestro Objeto también?

En el ejemplo a continuación, no podemos usar un operador de bucle o propagación for-of para extraer datos de nuestra Usersclase. Tenemos que usar un getmétodo personalizado .

Pero, ¿no sería bueno poder utilizar estos métodos existentes en nuestros propios objetos? Para lograr esto, necesitamos tener reglas que todos los desarrolladores puedan seguir y hacer que sus objetos funcionen con los métodos existentes.

Si siguen estas reglas para extraer datos de sus objetos, dichos objetos se denominan “iterables”.

Las reglas son:

  1. El objeto / clase principal debe almacenar algunos datos.
  2. El objeto / clase principal debe tener el símbolo global “conocido” symbol.iteratorcomo su propiedad que implementa un método específico según las reglas # 3 a # 6.
  3. Este symbol.iteratormétodo debe devolver otro objeto, un objeto “iterador”.
  4. Este objeto “iterador” debe tener un método llamado nextmétodo.
  5. El nextmétodo debe tener acceso a los datos almacenados en la regla n. ° 1.
  6. Y si llamamos iteratorObj.next(), debería devolver algunos datos almacenados de la regla n. ° 1, ya sea como formato si desea devolver más valores, o como si no deseara devolver más datos.{value:<stored data>, done: false}{done: true}

Si se siguen todas esas 6 reglas, entonces el objeto principal se llama como ” iterable ” de la regla # 1. El objeto que devolvió se llama un ” iterador “.

Echemos un vistazo a cómo podemos hacer que nuestro Usersobjeto sea iterable:

Haga clic para ampliar

Nota importante : si pasamos un operador de bucle o propagación iterableallUsersfor-of , internamente llaman <iterable>[Symbol.iterator]()para obtener el iterador (me gusta allUsersIterator) y luego usan el iterador para extraer datos.

Entonces, de alguna manera, todas esas reglas están ahí para tener una forma estándar de devolver un iteratorobjeto.

Funciones del generador

¿POR QUÉ?

Hay dos razones principales:

  1. proporcionar abstracción de mayor nivel a los iterables
  2. Proporcione flujo de control más nuevo para ayudar con cosas como “retrollamada”.

Vamos a verlos en detalle.

RAZÓN # 1 – Un contenedor para iterables

En lugar de hacer que nuestra clase / objeto sea una iterablesiguiendo todas esas reglas, podemos simplemente crear algo llamado método “Generador” para simplificar las cosas.

A continuación se encuentran algunos de los puntos principales acerca de los generadores:

  1. Los métodos de generador tienen una nueva *<myGenerator>sintaxis dentro de una clase, y las funciones de generador tienen la sintaxis function * myGenerator(){}.
  2. Llamar a los generadores myGenerator()devuelve un generatorobjeto que también implementa el iteratorprotocolo (reglas), por lo que podemos utilizar esto como un iteratorvalor de retorno out-of-the-box.
  3. Los generadores usan una yielddeclaración especial para devolver datos.
  4. yield las declaraciones siguen las llamadas anteriores y simplemente continúan desde donde las dejó.
  5. Si usa yielddentro de un bucle, solo se ejecutará una vez cada vez que llamemos al next()método en el iterador.

Ejemplo 1:

El siguiente código le muestra cómo puede usar un método generador ( *getIterator()) en lugar de usar el Symbol.iteratormétodo e implementar el nextmétodo que sigue todas las reglas.

Usando generadores dentro de una clase

Ejemplo 2:

Puedes simplificarlo aún más. Convierta una función en un generador (con * sintaxis), y utilícela yieldpara regresar los valores de uno en uno, como se muestra a continuación.

Usando generadores directamente como funciones

Nota importante : aunque en los ejemplos anteriores, estoy usando la palabra “iterador” para representar allUsers , realmente es un generatorobjeto.

¡El objeto generador tiene métodos como throwreturnademás del nextmétodo! Pero para fines prácticos, podemos usar el objeto devuelto como solo “iterador”.

RAZÓN # 2: Proporcione mejores y más nuevos flujos de control

Ayude a proporcionar nuevos flujos de control que nos ayuden a escribir programas de nuevas maneras y a resolver cosas como “infierno de devolución de llamada”.

Observe que a diferencia de una función normal, la función del generador puede yield(almacenar la función statey el returnvalor) y también estar preparado para tomar valores de entrada adicionales en el punto donde se produjo.

En la imagen siguiente, cada vez que ve yield, puede devolver el valor. Puede usar generator.next(“some new value”)y pasar el nuevo valor en el punto donde cedió.

Función normal vs función generador

El siguiente ejemplo muestra en términos más concretos cómo funciona el flujo de control:

Flujo de control del generador

Sintaxis y uso del generador

Las funciones del generador se pueden usar de las siguientes maneras:

Podemos tener más código después de “rendimiento” (a diferencia de la declaración de “retorno”)

Al igual que la returnpalabra clave, la yieldpalabra clave también devuelve el valor, ¡pero nos permite tener código después de ceder!

Puedes tener múltiples rendimientos

puede tener múltiples declaraciones de rendimiento

Enviar valores de ida y vuelta a los generadores a través del “próximo” método

El nextmétodo de iteradores también puede pasar valores nuevamente al generador como se muestra a continuación.

De hecho, esta característica permite a los generadores eliminar el “infierno de devolución de llamada”. Aprenderá más sobre esto en un momento.

Esta característica también se usa mucho en bibliotecas como redux-saga .

En el ejemplo siguiente, llamamos al iterador con una next()llamada vacía para obtener la pregunta. Y luego, pasamos 23como el valor cuando llamamos a next(23)la 2da vez.

Pasar el valor de regreso al generador desde el exterior a través de “siguiente”

Los generadores ayudan a eliminar el “infierno de devolución de llamada”

Sabes que nos metemos en el infierno de devolución de llamadas si tenemos múltiples llamadas asíncronas.

El siguiente ejemplo muestra cómo las bibliotecas, como ” co “, utilizan la función del generador que nos permite pasar un valor a través del nextmétodo para ayudarnos a escribir código asíncrono sincrónicamente.

Observe cómo la cofunción transfiere el resultado de la promesa al generador mediante el next(result)Paso 5 y el Paso 10.

Explicación paso a paso de libs como “co” que usa “next (<someval>)”

OK, pasemos a async / await.

ASYNC / AWAIT

¿POR QUÉ?

Como viste anteriormente, Generators puede ayudar a eliminar el “infierno de devolución de llamadas”, pero necesitas una biblioteca de terceros copara que eso suceda. Pero el “infierno de devolución de llamada” es un problema tan grande, el comité de ECMAScript decidió crear un envoltorio solo para ese aspecto de Generator y emitió las nuevas palabras clave async/await.

Las diferencias entre Generators y Async / Await son:

  1. async / await usa en awaitlugar de yield.
  2. await solo funciona con Promises.
  3. En lugar de function*, usa la async functionpalabra clave.

Entonces, async/awaites esencialmente un subconjunto de generadores y tiene un nuevo azúcar sintáctico.

La asyncpalabra clave le dice al compilador de JavaScript que trate la función de manera diferente. El compilador se detiene cada vez que alcanza la awaitpalabra clave dentro de esa función. Asume que la expresión después awaitdevuelve una promesa y espera hasta que la promesa se resuelva o se rechace antes de seguir adelante.

En el ejemplo siguiente, la getAmountfunción llama a dos funciones asíncronas getUsergetBankBalance . Podemos hacer esto en una promesa, pero el uso async awaites más elegante y simple.

ITERADORES ASINCRONICOS

¿POR QUÉ?

Es un escenario bastante común en el que necesitamos llamar a las funciones asincrónicas en un bucle. Por lo tanto, en ES2018 (propuesta completada), el comité TC39 ideó un nuevo Símbolo Symbol.asyncIteratory también una nueva construcción for-await-ofpara ayudarnos a recorrer fácilmente las funciones asíncronas.

La principal diferencia entre los objetos Iterator regulares y los Iteradores Async es la siguiente:

Objeto iterador

  1. El next()método del objeto Iterator devuelve valor como{value: ‘some val’, done: false}
  2. Uso: iterator.next() //{value: ‘some val’, done: false}

Objeto asincrónico iterador

  1. El método next () del objeto Async Iterator devuelve una Promesa que luego se convierte en algo así como{value: ‘some val’, done: false}
  2. Uso: iterator.next().then(({ value, done })=> {//{value: ‘some val’, done: false}}

El siguiente ejemplo muestra cómo for-await-offunciona y cómo puede usarlo.

for-await-of (ES2018)

RESUMEN

Símbolos  : proporcionan un tipo de datos global único. Se utilizan principalmente como propiedades del objeto para agregar nuevos comportamientos de modo que no se rompen métodos estándar como Object.keysfor-inbucles.

Símbolos conocidos  : son símbolos generados automáticamente por JavaScript y se pueden usar para implementar métodos centrales en nuestros objetos personalizados.

Iterables  : son todos los objetos que almacenan una colección de datos y siguen reglas específicas para que podamos utilizar operadores estándar de for-ofbucle y  ...extensión para extraer datos de ellos.

Iteradores  : son devueltos por Iterables y tienen el nextmétodo; es lo que realmente extrae los datos de un iterable.

Generadores : proporciona una abstracción de nivel superior a Iterables. También proporcionan nuevos flujos de control que pueden resolver cosas como retrollamada y proporcionar bloques de construcción para cosas como Async/Await.

Async / Await  : proporciona una mayor abstracción de nivel a los generadores para resolver específicamente el problema de devolución de llamada.

Iteradores  asíncronos: una nueva característica 2018 para ayudar a recorrer una serie de funciones asíncronas para obtener el resultado de cada función asíncrona como en un ciclo normal.

¡Eso es practicamente todo!

Otras lecturas

ECMAScript 2015+

  1. Aquí hay ejemplos de todo lo nuevo en ECMAScript 2016, 2017 y 2018
  2. Vea estos consejos y trucos útiles de ECMAScript 2015 (ES6)
  3. 5 JavaScript “malo” partes que se reparan en ES6
  4. ¿Es “Clase” en ES6 la nueva parte “mala”?
1 comment
  1. Viagra
    Viagra
    mayo 17, 2018 at 7:02 pm

    Gracias por el capitulo, Grande EvermorE 3viagra

    Reply
Leave a Reply

Your email address will not be published. Required fields are marked *

Ir a la barra de herramientas