Utilizzo di await al di fuori di una funzione async


86

Stavo tentando di concatenare due funzioni asincrone, perché la prima aveva un parametro di ritorno condizionale che causava l'esecuzione della seconda o l'uscita dal modulo. Tuttavia, ho riscontrato un comportamento strano che non riesco a trovare nelle specifiche.

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

Questo è uno snippet imbastardito del mio codice (puoi vedere l'intero ambito qui ), che controlla semplicemente se un giocatore è già in una lobby, ma è irrilevante.

Successivamente abbiamo questa funzione asincrona.

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

Questa funzione non deve essere eseguita se exit === true.

Ho provato a fare

const inLobby = await isInLobby();

Questo speravo attendesse i risultati, quindi posso usarlo inLobbyper eseguire in modo condizionale countPlayer, tuttavia ho ricevuto un errore di tipo senza dettagli specifici.

Perché non puoi awaituna asyncfunzione al di fuori dell'ambito della funzione? So che è una promessa di zucchero, quindi deve essere incatenato thenma perché dentro countPlayerposso aspettare un'altra promessa, ma fuori non posso await isInLobby?


Puoi mostrarci dove hai fatto await isInLobby()e come inLobbysi usa? Inoltre, dove / come si countPlayerchiama?
Bergi

@Bergi ho collegato il mio repo per il contesto attuale. Troppo codice da inserire nella domanda
Sterling Archer

Non vedo dove sia il problema (forse hai già aggiornato il repo)? Se ti riferisci alla isInLobby().then( … countPlayer().then …parte, la soluzione è banale: basta creare la funzione in cui sono contenute quelle chiamate ( (req, res) =>quella) async.
Bergi

@Bergi il problema non è che fosse rotto, funziona così com'è. Semplicemente non capivo perché l'attesa di alto livello non fosse una cosa. Si scopre che non esiste ancora senza definire l'ambito dell'intero modulo come funzione asincrona
Sterling Archer

Ma non hai nemmeno bisogno di un livello superiore await per il tuo codice? Ecco perché mi sono chiesto che tu abbia accettato la risposta che non si riferisce realmente al problema nella domanda.
Bergi

Risposte:


74

Il livello superiore awaitnon è supportato. Ci sono alcune discussioni da parte del comitato per gli standard sul motivo per cui questo è, come questo problema di GitHub .

C'è anche un pensiero su GitHub sul perché l'attesa di alto livello sia una cattiva idea. In particolare, suggerisce che se hai un codice come questo:

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

Ora qualsiasi file importato data.jsnon verrà eseguito fino al completamento del recupero, quindi tutto il caricamento del modulo è ora bloccato. Ciò rende molto difficile ragionare sull'ordine dei moduli dell'app, poiché siamo abituati a eseguire Javascript di primo livello in modo sincrono e prevedibile. Se ciò fosse consentito, sapere quando una funzione viene definita diventa complicato.

La mia prospettiva è che è una cattiva pratica che il tuo modulo abbia effetti collaterali semplicemente caricandolo. Ciò significa che qualsiasi consumatore del tuo modulo otterrà effetti collaterali semplicemente richiedendo il tuo modulo. Questo limita fortemente dove può essere usato il tuo modulo. Un livello superiore awaitprobabilmente significa che stai leggendo da qualche API o chiamando un servizio al momento del caricamento. Invece dovresti semplicemente esportare le funzioni asincrone che i consumatori possono utilizzare al proprio ritmo.


È un buon collegamento, grazie. È un peccato che il supporto di alto livello non sia presente. Lo spero Allo stato attuale, devo annidare le mie promesse qui e questa è una pessima pratica e non mi piace. :( Grazie.
Sterling Archer

@SterlingArcher in alternativa, usa un IIFE asincrono:void async function() { const inLobby = await isInLobby() }()
robertklep

@robertklep non sarebbe tale ambito inLobbyper la funzione rendendola non accessibile?
Sterling Archer

@SterlingArcher sì, lo farebbe, richiederebbe di spostare tutto il codice lì (fondamentalmente rendendolo il "livello superiore"). È solo un'alternativa all'uso .then()(scusate, avrei dovuto renderlo un po 'più chiaro).
robertklep

Non sono d'accordo, l'utente di data.js è 'bloccato' tuttavia durante il caricamento di data.js stesso e di tutte le sue dipendenze, questa nozione di 'blocco' non è male di per sé. L'attesa di livello superiore può essere considerata come il caricamento di alcune dipendenze che apparentemente si devono avere prima che l'utilizzo venga "rilasciato".
Ibrahim ben Salah

129

Ovviamente c'è sempre questo:

(async () => {
    await ...

    // all of the script.... 

})();
// nothing else

Questo crea una funzione rapida con async in cui puoi usare wait. Ti evita la necessità di creare una funzione asincrona, il che è fantastico! // crediti Silve2611


3
Si prega di elaborare ulteriormente e spiegare perché funziona.
Paul Back,

12
è molto stupido Downvote questa risposta. Esegue una funzione rapida con async in cui è possibile utilizzare await. Ti evita la necessità di creare una funzione asincrona, il che è fantastico!
Silve2611

55
Non risolve il problema poiché è ancora necessaria awaitquesta funzione anonima, che ancora una volta non funziona dall'esterno delle funzioni.
Michael

quindi come gestirebbe una condizione di errore? atterra anche all'interno del codice di attesa?
Manuel Hernandez

Tks questo è un grande aiuto, specialmente per la promessa di richiamata una volta recuperata l'API di accesso, che risponde perfettamente per ottenere lo storage getItem
hsu

10

Ancora meglio è inserire un punto e virgola aggiuntivo davanti al blocco di codice

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

Ciò impedisce al formattatore automatico (ad esempio in vscode) di spostare la prima parentesi alla fine della riga precedente.

Il problema può essere dimostrato nel seguente esempio:

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

Senza il punto e virgola, questo verrà riformattato come:

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

il che ovviamente è sbagliato perché assegna la funzione asincrona come yparametro e cerca di chiamare una funzione dal risultato (che in realtà è una stringa strana '1async () => {...}')


20
Non è il riformattatore che è sbagliato. Senza punto e virgola, è così che la tua funzione verrebbe analizzata in runtime e otterrai un errore, un'interruzione di riga o nessuna interruzione di riga. La soluzione corretta nel tuo esempio sarebbe aggiungere un punto add(1);e virgola dopo e non prima della funzione asincrona.
Il fantasma di Madara

11
Inoltre, onestamente non capisco come questo sia collegato alla domanda in questione.
Il fantasma di Madara il

Sì hai ragione. Anche il runtime necessita del punto e virgola.
Viliam Simko


1

puoi fare il massimo attendo dal dattiloscritto 3.8
https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-8.html#-top-level-await
Dal post:
Questo perché in precedenza in JavaScript (insieme alla maggior parte degli altri linguaggi con una funzionalità simile), await era consentito solo all'interno del corpo di una funzione asincrona. Tuttavia, con il livello più alto di await, possiamo usare await al livello più alto di un modulo.

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

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

Nota che c'è una sottigliezza: il primo livello di attesa funziona solo al livello più alto di un modulo e i file sono considerati moduli solo quando TypeScript trova un'importazione o un'esportazione. In alcuni casi di base, potresti dover scrivere export {} come un boilerplate per assicurarti di questo.

Il massimo livello di attesa potrebbe non funzionare in tutti gli ambienti in cui potresti aspettarti a questo punto. Al momento, puoi usare il primo livello di attesa solo quando l'opzione del compilatore di destinazione è es2017 o superiore e il modulo è esnext o system. Il supporto in diversi ambienti e bundler può essere limitato o può richiedere l'abilitazione del supporto sperimentale.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.