Una breve historia de las características asincrónicas de Javascript

Mientras estudiaba Javascript, me encontré con numerosos artículos sobre funciones y operaciones asincrónicas una y otra vez. A pesar de las indudables ventajas de un dispositivo tan funcional, cada vez que me encontraba en dificultades la lista citada por los autores. Las palabras cambiaron, la esencia permaneció igual, las gachas se estaban gestando en mi cabeza. Under the cut: una pequeña guía para el desarrollo histórico y las versiones de ECMA.

¿Por qué necesitamos operaciones asincrónicas?


Un programa de computadora puede realizar un número ilimitado de tareas. No es ningún secreto que las aplicaciones web deben funcionar con muchas tareas diferentes, que a menudo necesitan usar los mismos datos. En particular, uno de los ejemplos más comunes es mostrar información de usuario (UI) y recuperar información mediante solicitudes del servidor. No es sorprendente que casi todos los desarrolladores web se enfrenten a esto: trabajar con una base de datos determinada, proporcionar una interfaz de usuario, organizar algunas API; todo esto está literalmente en todas las tareas de prueba de no solo los programadores de JS.

¿Por qué no ejecutar comandos secuencialmente?

A menudo, la información que necesita el usuario solo se puede obtener después de un período de tiempo considerable. Si organiza el programa como:

  1. Obteniendo información del sitio https: / some / api / item / 1
  2. Mostrar información sobre el primer elemento en la pantalla.

Surgirán serias dificultades al presentar la página y crear una impresión agradable en el usuario (la denominada experiencia del usuario). Imagínese: una página, por ejemplo, Netflix o Aliexpress tendrá que obtener datos de cientos de bases de datos antes de comenzar a mostrar los contenidos al usuario. Tal demora será similar a cargar un nivel de juego en 3D, y si el jugador está listo para esperar, el usuario del sitio web quiere obtener la mayor cantidad de información en este momento.

Se encontró la solución: operaciones asincrónicas . Mientras que el hilo principal del programa está ocupado inicializando y mostrando elementos del sitio web en el lienzo, también envía tareas a los otros hilos en el espíritu de " obtener los Bienes para el Usuario ". Tan pronto como este hilo completa su trabajo, la información se "instala" en el hilo principal, y está disponible para su visualización, y en la página web hay un cierto marcador de posición, un objeto que ocupa espacio para información futura.

imagen

En este punto, la página ya se muestra, a pesar de que algunas solicitudes aún no se han aprobado.

imagen

Lo más probable es que, en algún lugar en la parte inferior de la página, algunas solicitudes más devuelvan un valor, y la página continúa actualizándose y procesándose dinámicamente, sin ningún inconveniente para el usuario.

ES5 y versiones anteriores: devolución de llamada


Antes de continuar con la revisión de las devoluciones de llamada, echemos un vistazo / descubramos qué funciones son de un orden superior .

Una función de orden superior en JS es una función que toma otra función como argumento . Aquí hay un ejemplo:

objectIsString(objectRef) { return typeof(objectRef) === 'String'; } listOfObjects.filter(objectIsString); 

Por lo tanto, la función objectIsString se pasó a la función de orden superior - filtro - que permite filtrar listOfObjects y dejar solo objetos de tipo cadena en la lista.
Las devoluciones de llamada funcionan de manera similar. Esta es una función pasada como argumento a otra función. Muy a menudo, la función setTimeout se usa como un ejemplo de una función que procesa devoluciones de llamada. En general, esto se usa como setTimeout (function, timeoutValue), donde function es una función de devolución de llamada ejecutada por el navegador después de un período de tiempo especificado en timeout.

 setTimeout(console.log(1), 2000); console.log(2); 


Imprimir 2 1.

ES 6: Promesas


En el Estándar 6, se introdujo un nuevo tipo: Promesa (promesa, en adelante, Promesa). Una promesa es un tipo cuyos objetos tienen uno de tres estados: pendiente, cumplido, rechazado. Además, con los dos últimos estados puede "asociar" funciones: devoluciones de llamada. Tan pronto como el proceso asincrónico descrito en el marco de la promesa en sí llega al éxito / fracaso, se llamará a la función asociada a él. Este proceso se llama "devoluciones de llamada suspendidas, y se realiza utilizando los métodos then y catch de la promesa misma. La diferencia es que cuando llama a los argumentos, se transfieren dos funciones: en caso de éxito (onFullfillment) y falla (onRejected), mientras que catch acepta, como no es difícil de adivinar, solo una función para procesar errores en una promesa. Para determinar si una promesa se ejecutó con éxito en un caso particular, así como para parametrizar el resultado devuelto

Creemos y usemos una promesa por etapas.

 // : let promise; //     Promise. let promise = new Promise((resolve, reject) => { }); //  ,  . let promise = new Promise((resolve, reject) => { setTimeout(() => { resolve("result"); }, 1000); }); 

Ahora agregue controladores de eventos utilizando el método then. El argumento a la función que maneja el éxito es el resultado, mientras que el argumento a la función para manejar el fracaso de la promesa es el error.

 promise .then( result => { }, error => { } ); //     – . promise .then( result => { //  - -    resolve alert("Fulfilled: " + result); // result -  resolve }, error => { //   -    reject alert("Rejected: " + error); // error -  reject } ); 

Hecho

Entonces, describiremos una vez más el proceso de crear una promesa brevemente:

  1. Inicializar el objeto (nueva promesa)
  2. Le pasamos la función de resolver y / o rechazar como único argumento al constructor. Una función debe tener al menos 1 operación asincrónica
  3. Usando los métodos then / catch, agregamos funciones - manejadores de resultados.

Generadores. Rendimiento


También en el estándar ES6, se definió un nuevo tipo de función: los generadores. Estas funciones tienen la capacidad de devolver diferentes valores varias veces con llamadas idénticas a primera vista. Veamos cómo lo hacen y por qué usarlo.

La forma estándar del generador: function * functionName () {}. En el cuerpo de las funciones mismas, la palabra rendimiento se usa para devolver un valor intermedio.

Como ejemplo, considere el siguiente generador:

 function* generateNumber() { yield 1; yield 2; return 3; } 

Por el momento, el generador está al comienzo de su ejecución. Cada vez que se llama al siguiente método generador, se ejecutará el código descrito antes del siguiente rendimiento (o retorno), y también se devolverá el valor indicado en la línea con una de estas palabras.

 Let one = generateNumber.next(); // {value: 1, done: false} 

La siguiente llamada devolverá el valor 2 de la misma manera, la tercera llamada devolverá el valor 3 y finalizará la ejecución de la función.

 Let two = generateNumber.next(); // {value: 2, done: false} Let three = generateNumber.next(); // {value: 3, done: false} 

A pesar de esto, aún se puede acceder al generador a través de la siguiente función. Sin embargo, devolverá el mismo valor: el objeto {done: true}.

ES7. Asíncrono / espera


Junto con el deseo de complacer a los amantes de OOP con la ayuda de clases de azúcar sintácticas y la imitación de la herencia, los creadores de ES7 están tratando de facilitar la comprensión de JavaScript y que los amantes escriban código sincrónico. Mediante el uso de construcciones asíncronas / en espera, el usuario puede escribir código asíncrono lo más similar posible a síncrono. Si lo desea, puede deshacerse de las promesas estudiadas recientemente y volver a escribir el código con cambios mínimos.
Considere un ejemplo:

Usando promesas:

 requestBook(id) { return bookAPIHelper.getBook(id).then(book => {console.log(book)}); } 

Usando async / await.

 async requestBook(id) { Const book = await bookAPIHelper.getBook(id); Console.log(book); } 

Describamos lo que vimos:

1) Asíncrono: palabra clave agregada al declarar una función asincrónica
2) Aguardar: una palabra clave agregada al llamar a una función asincrónica.

ES8. Iteración asincrónica


Iterar sobre los datos sincrónicamente se hizo posible en ES5. Después de dos especificaciones, se decidió agregar la posibilidad de que la iteración asincrónica funcione en fuentes de datos asincrónicas. Ahora, al llamar a next (), devolverá no {value, done}, sino una promesa (ver ES6).

Veamos la función createAsyncIterable (iterable).

 async function* createAsyncIterable(iterable) { for (const elem of iterable) { yield elem; } } 

Como puede ver, la función inicializa la colección, para cada llamada a los elementos de los cuales se devolverá una promesa con el valor especificado en iterable.

 const asyncIterable = createAsyncIterable(['async 1', 'async 2']); const asyncIterator = asyncIterable[Symbol.asyncIterator](); asyncIterator.next() .then(result => { console.log(result); // { // value: 'async 1', // done: false, // } return asyncIterator.next(); }) .then(result => { console.log(result); // { // value: 'async 2', // done: false, // } return asyncIterator.next(); }) .then(result => { console.log(result); // { // value: 'undefined', // done: true, // } }); 

Además, el nuevo estándar define un bucle de espera que es conveniente para tales operaciones.

 for await (const x of createAsyncIterable(['a', 'b'])) 

TL; DR


No es necesario saber y recordar de memoria a qué versión de ECMAScript pertenece esta o esa sintaxis, especialmente si acaba de comenzar a conocer el comportamiento asincrónico en JS. Al mismo tiempo, estudiar la asincronía exactamente en el orden propuesto por la historia del desarrollo de las especificaciones permitirá al programador no solo comprender perfectamente la sintaxis y las instrucciones pasadas al motor JS, sino también seguir la lógica de mejorar ECMAScript como producto, comprender las tendencias dictadas por los desarrolladores de JS, separarlos y aceptar .

En resumen, entonces:

Callbacks <= ES5
Promesas, rendimiento (generadores): ES6
Asíncrono / espera: ES7
Iteradores asíncronos: ES8

Source: https://habr.com/ru/post/es434140/


All Articles