Usando await fuera de una función asincrónica

85

Estaba intentando encadenar dos funciones asíncronas juntas, porque la primera tenía un parámetro de retorno condicional que hacía que la segunda se ejecutara o saliera del módulo. Sin embargo, encontré un comportamiento extraño que no puedo encontrar en las especificaciones.

async function isInLobby() {
    //promise.all([chained methods here])
    let exit = false;
    if (someCondition) exit = true;
}

Este es un fragmento bastardo de mi código (puede ver el alcance completo aquí ), que simplemente verifica si un jugador ya está en un lobby, pero eso es irrelevante.

A continuación, tenemos esta función asincrónica.

async function countPlayer() {
    const keyLength = await scardAsync(game);
    return keyLength;
}

Esta función no necesita ejecutarse si exit === true.

Traté de hacer

const inLobby = await isInLobby();

Esto esperaba que esperara los resultados, por lo que puedo usarlo inLobbypara ejecutar condicionalmente countPlayer, sin embargo, recibí un error de tipo sin detalles específicos.

¿Por qué no puede realizar awaituna asyncfunción fuera del alcance de la función? Sé que es una promesa de azúcar, por lo que debe estar encadenada, thenpero ¿por qué countPlayerpuedo esperar otra promesa adentro , pero afuera no puedo await isInLobby?

Arquero esterlina
fuente
¿Puede mostrarnos dónde lo hizo await isInLobby()y cómo inLobbyse usa? Además, ¿dónde / cómo se countPlayerllama?
Bergi
@Bergi Vinculé mi repositorio para el contexto real. Demasiado código para poner en cuestión
Sterling Archer
No veo dónde está el problema con eso (tal vez ya actualizó el repositorio). Si se refiere a la isInLobby().then( … countPlayer().then …parte, la solución es trivial: simplemente haga la función en la que están contenidas esas llamadas (la (req, res) =>única) async.
Bergi
@Bergi, el problema no es que esté roto, funciona como está. Simplemente no entendía por qué la espera de alto nivel no era una cosa. Resulta que aún no existe sin el alcance de todo el módulo como una función asíncrona
Sterling Archer
¿Pero ni siquiera necesitas un nivel superior await para tu código? Es por eso que me pregunto si aceptaste la respuesta que realmente no se relaciona con el problema de la pregunta.
Bergi

Respuestas:

73

El nivel superior awaitno es compatible. Hay algunas discusiones por parte del comité de estándares sobre por qué esto es así, como este problema de Github .

También hay un artículo de reflexión en Github sobre por qué esperar de nivel superior es una mala idea. Específicamente, sugiere que si tiene un código como este:

// data.js
const data = await fetch( '/data.json' );
export default data;

Ahora, cualquier archivo que importe data.jsno se ejecutará hasta que se complete la recuperación, por lo que toda la carga de su módulo ahora está bloqueada. Esto hace que sea muy difícil razonar sobre el orden de los módulos de la aplicación, ya que estamos acostumbrados a que Javascript de nivel superior se ejecute de forma sincrónica y predecible. Si esto estuviera permitido, saber cuándo se define una función se vuelve complicado.

Mi perspectiva es que es una mala práctica que su módulo tenga efectos secundarios simplemente cargándolo. Eso significa que cualquier consumidor de su módulo tendrá efectos secundarios simplemente al requerir su módulo. Esto limita gravemente dónde se puede utilizar su módulo. Un nivel superior awaitprobablemente significa que está leyendo desde alguna API o llamando a algún servicio en el momento de la carga. En su lugar, debería exportar funciones asíncronas que los consumidores puedan utilizar a su propio ritmo.

Andy Ray
fuente
Ese es un buen enlace, gracias. Es una pena que no haya soporte de alto nivel. Espero que lo sea. Actualmente, tal como está, tengo que anidar mis promesas aquí y esa es una muy mala práctica y no me gusta. :( Gracias.
Sterling Archer
@SterlingArcher alternativamente, use un IIFE asíncrono:void async function() { const inLobby = await isInLobby() }()
robertklep
@robertklep, ¿no tendría ese alcance inLobbypara la función que la hace no accesible?
Sterling Archer
@SterlingArcher sí lo haría, requeriría que mueva todo su código allí (básicamente haciéndolo el "nivel superior"). Es solo una alternativa al uso .then()(lo siento, debería haberlo dejado un poco más claro).
robertklep
no estoy de acuerdo, el usuario de data.js está 'bloqueado'; sin embargo, mientras carga data.js y todas sus dependencias, esta noción de 'bloqueo' no es mala por sí misma. La espera de nivel superior podría considerarse como la carga de alguna dependencia que aparentemente se necesita tener antes de que se 'libere' el uso.
Ibrahim ben Salah
125

Siempre hay esto, por supuesto:

(async () => {
    await ...

    // all of the script.... 

})();
// nothing else

Esto hace una función rápida con async donde puede usar await. ¡Te ahorra la necesidad de hacer una función asíncrona que es genial! // créditos Silve2611

digerati-estratagies
fuente
3
Desarrolle más y explique por qué funciona.
Paul Back
12
es muy estúpido rechazar esta respuesta. Hace una función rápida con async donde puedes usar await. ¡Te ahorra la necesidad de hacer una función asíncrona que es genial!
Silve2611
55
No resuelve el problema ya que aún necesita awaitesta función anónima, que nuevamente, no funciona desde fuera de las funciones.
Michael
entonces, ¿cómo manejaría esto una condición de error? ¿También aterriza dentro del código de espera?
Manuel Hernandez
Tks, esta es una gran ayuda, especialmente para la promesa de devolución de llamada una vez que se obtiene la API de inicio de sesión, que se responde perfectamente para obtener almacenamiento getItem
hsu
9

Aún mejor es poner un punto y coma adicional delante del bloque de código

;(async () => {
    await ...
})();

Esto evita que el formateador automático (por ejemplo, en vscode) mueva el primer paréntesis al final de la línea anterior.

El problema se puede demostrar en el siguiente ejemplo:

const add = x => y => x+y
const increment = add(1)
(async () => {
    await ...
})();

Sin el punto y coma, se volverá a formatear como:

const add = x => y => x+y
const increment = add(1)(async () => {
  await Promise(1)
})()

lo cual obviamente es incorrecto porque asigna la función asíncrona como yparámetro e intenta llamar a una función desde el resultado (que en realidad es una cadena extraña '1async () => {...}')

Viliam Simko
fuente
20
No es el reformateador el que está mal. Sin un punto y coma, así es como se analizaría su función en tiempo de ejecución y obtendría un error, un salto de línea o ningún salto de línea. La solución correcta en su ejemplo sería agregar un punto add(1);y coma después y no antes de la función asincrónica.
Madara's Ghost
11
Además, honestamente, no entiendo cómo esto se relaciona con la pregunta en cuestión.
Madara's Ghost
Sí tienes razón. Además, el tiempo de ejecución necesita el punto y coma.
Viliam Simko
1

puede hacer espera de nivel superior desde mecanografiado 3.8
https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-8.html#-top-level-await
De la publicación:
Esto se debe a que anteriormente en JavaScript (junto con la mayoría de los otros lenguajes con una característica similar), await solo se permitía dentro del cuerpo de una función asincrónica. Sin embargo, con await de nivel superior, podemos usar await en el nivel superior de un módulo.

const response = await fetch("...");
const greeting = await response.text();
console.log(greeting);

// Make sure we're a module
export {};

Tenga en cuenta que hay una sutileza: la espera de nivel superior solo funciona en el nivel superior de un módulo, y los archivos solo se consideran módulos cuando TypeScript encuentra una importación o una exportación. En algunos casos básicos, es posible que deba escribir export {} como un texto estándar para asegurarse de esto.

Es posible que la espera de nivel superior no funcione en todos los entornos en los que podría esperar en este momento. Actualmente, solo puede usar el nivel superior de espera cuando la opción del compilador de destino es es2017 o superior, y el módulo es esnext o system. El soporte dentro de varios entornos y paquetes puede ser limitado o puede requerir la habilitación del soporte experimental.

HACHA
fuente