Uno tiene que usar la palabra clave asíncrona en la función contenedora para usar la espera dentro del cuerpo de la función.
async function fetchMovies() { const response = await fetch('/movies'); console.log(response); } fetchMovies();El await se usa para bloquear al finalizar la llamada fetch() asíncrona. Como se puede ver en el código, la función fetchMovies() ni siquiera devuelve ningún valor. E incluso si lo hiciera, afectaría la forma en que la persona que llama consume el valor de retorno, pero ¿por qué debería importar la llamada a otra llamada asincrónica desde el cuerpo de la función?
Mi pregunta es ¿por qué es necesario? ¿Hay alguna buena explicación de eso? ¿Está relacionado con la necesidad de una implementación real de await y la compatibilidad con versiones anteriores de JavaScript?
Sé que se usa el patrón iffi para poder usar await , pero ¿eso cambia la semántica del código que sigue al bloque de código iffi de alguna manera?
(async () => { const response = await fetch('/movies'); console.log(response); })();También soy consciente de que el nivel superior espera ser compatible con los módulos.
Puede ser que me esté perdiendo algo realmente obvio.
Supongo que su pregunta exacta es esta: " El manejo de los valores devueltos (nulos o algo así) depende del consumidor que llamó a la función. "Deberían" supuestamente obtenerla incluso si se llama a otra función asíncrona en el medio. Entonces, ¿por qué es importante esperar antes de otras llamadas asíncronas? "
Verá, tales llamadas fetch() se realizan en las bases de datos dentro de la duración de "iniciar" y "cerrar" la conexión. Casi todos los métodos utilizados son asíncronos en este caso. Entonces, mientras ejecutas fetchMovies(); El hilo podría avanzar más y ejecutar connection.close(); antes de que la recuperación se resuelva y se devuelva.
Los escenarios exactos son así:
await client.connect(); //connection establishment // Your purposeful methods async function fetchMovies() { const response = await fetch('/movies'); console.log(response); } await fetchMovies(); // Closing connection to avoid attacks and maintain concurrency await client.close(); Si algún método, en este caso, se llama de forma asíncrona, se desperdicia toda la sesión de una conexión de base de datos y nuestra función devolvería undefined o arrojaría un error "No se puede usar una sesión que ha finalizado"
Por lo tanto, debemos "esperar" a que las Promesas "Pendientes" alcancen el estado "Cumplido" o "Rechazado" antes de ejecutar más llamadas.
Puede consultar más en este artículo: Usando Promises, async / await con MongoDB solo por el bien de la comprensión.
Hay tres razones por las que existe la palabra clave async :
En las versiones del lenguaje ECMAScript anteriores a 2015, await no era una palabra clave. Marcar una función como async proporciona un "rescate" sintáctico para indicar un cambio importante en la gramática del lenguaje dentro del cuerpo de la función.
Esta es la razón más importante. Sin la palabra clave async , todos los programas escritos en ECMAScript 5 o anterior ya no funcionarían si usaran la palabra clave await como una variable (de hecho , esto se hizo intencionalmente en algunos casos como un polyfill antes de que se estandarizara async / await ), ya que eso provocar un cambio importante sin la adición de async a la especificación. Debido a esto, async es sintácticamente necesario para evitar cambios bruscos en el idioma .
Proporciona un marcador conveniente para los analizadores, evitando una búsqueda infinita para determinar si una función es asíncrona o no.
Esto hace que el análisis sea más eficiente, lo que resulta atractivo tanto para los implementadores como para los desarrolladores de ECMAScript, aunque este motivo por sí solo no hace que la async sea estrictamente necesaria para la sintaxis.
async también realiza su propia transformación en la función, que se realiza independientemente de si la palabra clave await está presente o no en el cuerpo.
Considere las siguientes dos funciones:
function foo() { if (Math.random() < 0.5) { return 'return'; } else { throw 'throw'; } } async function bar() { if (Math.random() < 0.5) { return 'return'; } else { throw 'throw'; } } async realiza la siguiente transformación de la function bar() :
function bar() { return new Promise((resolve, reject) => { try { resolve((/*async function bar*/() => { if (Math.random() < 0.5) { return 'return'; } else { throw 'throw'; } })()); } catch (reason) { reject(reason); } }); }Aquellos familiarizados con las promesas reconocerán que podemos simplificar lo anterior ya que la función ejecutora del constructor Promise rechazará implícitamente si arroja un error sincrónicamente:
function bar() { return new Promise((resolve) => { if (Math.random() < 0.5) { return resolve('return'); } else { throw 'throw'; } }); }Creo que es para dejar claro que la función contiene código asíncrono. Usemos otro ejemplo que sí devuelve algo:
async function canUseGeolocation() { const result = await navigator.permissions.query({name: 'geolocation'}); return result.state; } La palabra clave async le dice al motor de javascript que la función debe devolver una promesa, y cualquier declaración de devolución en la función debe resolver esa promesa. ¿Qué sucede si lo modificamos para almacenar en caché los valores para que no siempre llamemos a la espera?
function canUseGeolocation() { if (cachedPermissionState) return cachedPermissionState; const result = await navigator.permissions.query({name: 'geolocation'}); cachedPermissionState = result.state; return result.state; } ¿Cómo debe javascript saber que la función debe devolver una promesa? Porque contiene un await ? ¿Qué sucede si luego cambia la función para que cachedPermissionState se establezca en otro lugar y esta función solo devuelve ese valor para que elimine la await ? ¿Esa primera declaración de devolución debería devolver una promesa o devolver el valor? Eso ahora cambiaría la forma en que se ejecuta la función y lo que devuelve return cachedPermissionState; . Al usar async , podemos saber que realmente devuelve una promesa de que la declaración de devolución se resuelve sin escanear la función en busca de declaraciones de await para determinar si debe tratarse como async o no.
Aquí se hacen dos preguntas. ¿Por qué necesita la palabra clave asíncrona para indicar un contexto asíncrono y por qué necesita un contexto asíncrono para usar await?
Si no usáramos la palabra clave async para indicar un contexto asíncrono, tendríamos problemas de compatibilidad con versiones anteriores. Antes de la actualización async/await, "await" era un nombre de variable válido. Entonces, para convertirlo en una palabra reservada, debemos introducir un nuevo contexto, el contexto de la función asíncrona.
La segunda pregunta es, ¿por qué queremos esto en primer lugar? ¿Por qué queremos diferenciar las funciones asíncronas del código síncrono tradicional? Como su nombre lo indica, las funciones asíncronas no ejecutan todo su código a la vez. Cuando una función "espera", detiene efectivamente la ejecución y el resto del código se convierte en un controlador de eventos para la cosa que se espera. Esto permite que se ejecute otro código.
Las implementaciones de javascript del navegador son de un solo subproceso. Solo se puede realizar una cosa a la vez, pero la cola de eventos permite que las funciones se turnen. Considere lo que sucedería si pudiera esperar de una función síncrona. El código síncrono, por definición, no cede el control. Por lo tanto, la función asíncrona que está esperando nunca tendrá la oportunidad de cambiar a su subproceso de ejecución único, y estará esperando para siempre. Por lo tanto, solo puede esperar una función asíncrona si ya se encuentra en un contexto asíncrono.