Verwenden von await außerhalb einer asynchronen Funktion

86

Ich habe versucht, zwei asynchrone Funktionen miteinander zu verketten, da die erste einen bedingten Rückgabeparameter hatte, der dazu führte, dass die zweite entweder ausgeführt wurde oder das Modul verließ. Ich habe jedoch ein merkwürdiges Verhalten gefunden, das ich in den Spezifikationen nicht finden kann.

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

Dies ist ein bastardisierter Ausschnitt meines Codes (Sie können den vollen Umfang hier sehen ), der einfach prüft, ob sich ein Spieler bereits in einer Lobby befindet, aber das ist irrelevant.

Als nächstes haben wir diese asynchrone Funktion.

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

Diese Funktion muss nicht ausgeführt werden, wenn exit === true.

Ich habe es versucht

const inLobby = await isInLobby();

Ich hoffte, dass dies auf Ergebnisse warten würde, damit ich inLobbyes bedingt ausführen kann countPlayer, erhielt jedoch einen Typfehler ohne spezifische Details.

Warum können Sie awaitkeine asyncFunktion außerhalb des Funktionsumfangs ausführen? Ich weiß, dass es ein Zuckerversprechen ist, also muss es angekettet werden, thenaber warum countPlayerkann ich auf ein anderes Versprechen warten, aber draußen kann ich nicht await isInLobby?

Sterling Archer
quelle
Können Sie uns zeigen, wo Sie es getan await isInLobby()haben und wie inLobbyes verwendet wird? Auch wo / wie countPlayerheißt das?
Bergi
@Bergi Ich habe mein Repo für den tatsächlichen Kontext verlinkt. Zu viel Code, um in die Frage gestellt zu werden
Sterling Archer
Ich sehe nicht, wo das Problem damit liegt (vielleicht haben Sie das Repo bereits aktualisiert)? Wenn Sie sich auf das isInLobby().then( … countPlayer().then …Teil beziehen , ist die Lösung trivial: Machen Sie einfach die Funktion, in der diese Aufrufe enthalten sind (die (req, res) =>eine) async.
Bergi
@Bergi das Problem ist nicht, dass es kaputt war, es funktioniert wie es ist. Ich habe einfach nicht verstanden, warum das Warten auf höchster Ebene keine Sache ist. Es stellt sich heraus, dass es noch nicht existiert, ohne Ihr gesamtes Modul als asynchrone Funktion zu definieren
Sterling Archer,
Aber Sie brauchen nicht einmal Top-Level await für Ihren Code? Deshalb habe ich mich gefragt, ob Sie die Antwort akzeptiert haben, die sich nicht wirklich auf das Problem in der Frage bezieht.
Bergi

Antworten:

73

Die oberste Ebene awaitwird nicht unterstützt. Es gibt einige Diskussionen des Normungsausschusses darüber, warum dies so ist, wie zum Beispiel diese Github-Ausgabe .

Es gibt auch einen Gedanken auf Github darüber, warum das Warten auf höchster Ebene eine schlechte Idee ist. Insbesondere schlägt er vor, dass, wenn Sie Code wie diesen haben:

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

Jetzt wird jede importierte Datei data.jserst ausgeführt, wenn der Abruf abgeschlossen ist, sodass das Laden Ihres Moduls jetzt blockiert ist. Dies macht es sehr schwierig, über die Reihenfolge der App-Module nachzudenken, da wir es gewohnt sind, Javascript auf höchster Ebene synchron und vorhersehbar auszuführen. Wenn dies zulässig wäre, wird es schwierig zu wissen, wann eine Funktion definiert wird.

Meiner Ansicht nach ist es eine schlechte Praxis für Ihr Modul, Nebenwirkungen zu haben, indem Sie es einfach laden. Das bedeutet, dass jeder Verbraucher Ihres Moduls Nebenwirkungen erhält, indem er einfach Ihr Modul benötigt. Dies schränkt die Verwendungsmöglichkeiten Ihres Moduls stark ein. Eine oberste Ebene awaitbedeutet wahrscheinlich, dass Sie beim Laden von einer API lesen oder einen Dienst aufrufen . Stattdessen sollten Sie nur asynchrone Funktionen exportieren, die Verbraucher in ihrem eigenen Tempo verwenden können.

Andy Ray
quelle
Das ist ein guter Link, danke. Es ist eine Schande, dass Top-Level-Support nicht da ist. Ich hoffe es ist. Derzeit muss ich meine Versprechen hier verschachteln und das ist eine sehr schlechte Praxis und ich mag es nicht. :( Danke.
Sterling Archer
@SterlingArcher alternativ verwenden Sie eine asynchrone IIFE:void async function() { const inLobby = await isInLobby() }()
Robertklep
@robertklep würde dieser Bereich inLobbyder Funktion nicht dazu führen, dass sie nicht zugänglich ist?
Sterling Archer
@SterlingArcher Ja, es würde erfordern, dass Sie Ihren gesamten Code dorthin verschieben (was ihn im Grunde zur "obersten Ebene" macht). Es ist nur eine Alternative zur Verwendung .then()(sorry, hätte das etwas klarer machen sollen).
Robertklep
stimme nicht zu, der Benutzer von data.js wird dennoch "blockiert", während data.js selbst und alle seine Abhängigkeiten geladen werden. Dieser Begriff des "Blockierens" ist an sich nicht schlecht. Das Warten auf oberster Ebene kann als Laden einer Abhängigkeit angesehen werden, die anscheinend benötigt wird, bevor die Verwendung "freigegeben" wird.
Ibrahim ben Salah
125

Das gibt es natürlich immer:

(async () => {
    await ...

    // all of the script.... 

})();
// nothing else

Dies macht eine schnelle Funktion mit Async, wo Sie warten verwenden können. Es erspart Ihnen die Notwendigkeit, eine asynchrone Funktion zu erstellen, die großartig ist! // Credits Silve2611

Digerati-Strategien
quelle
3
Bitte erläutern Sie weiter und erklären Sie, warum dies funktioniert.
Paul Back
12
Es ist sehr dumm, diese Antwort abzulehnen. Es macht eine schnelle Funktion mit Async, wo Sie warten können. Es erspart Ihnen die Notwendigkeit, eine asynchrone Funktion zu erstellen, die großartig ist!
Silve2611
55
Löst das Problem nicht, da Sie immer noch awaitdiese anonyme Funktion benötigen , die wiederum außerhalb von Funktionen nicht funktioniert.
Michael
Wie würde dies mit einer Fehlerbedingung umgehen? landet es auch im Wartecode?
Manuel Hernandez
TKS ist dies greate Hilfe, speziell Versprechen Rückruf einmal Login API holen, die reponse perfekt ist Abseite getItem zu bekommen
tess Hsu
9

Noch besser ist es, ein zusätzliches Semikolon vor den Codeblock zu setzen

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

Dies verhindert, dass der automatische Formatierer (z. B. in vscode) den ersten Parenthese an das Ende der vorherigen Zeile verschiebt.

Das Problem kann am folgenden Beispiel demonstriert werden:

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

Ohne das Semikolon wird dies wie folgt neu formatiert:

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

Das ist offensichtlich falsch, weil es die asynchrone Funktion als yParameter zuweist und versucht, eine Funktion aus dem Ergebnis aufzurufen (was eigentlich eine seltsame Zeichenfolge ist '1async () => {...}').

Viliam Simko
quelle
20
Es ist nicht der Reformatter, der falsch ist. Ohne ein Semikolon würde Ihre Funktion auf diese Weise zur Laufzeit analysiert, und Sie würden einen Fehler, einen Zeilenumbruch oder keinen Zeilenumbruch erhalten. Die richtige Lösung in Ihrem Beispiel wäre, nach add(1);und nicht vor der asynchronen Funktion ein Semikolon hinzuzufügen .
Geist
11
Außerdem verstehe ich ehrlich gesagt nicht, wie dies mit der vorliegenden Frage zusammenhängt.
Madaras Geist
Ja, du hast Recht. Auch die Laufzeit benötigt das Semikolon.
Viliam Simko
1

Sie können auf oberster Ebene warten, da Typoskript 3.8
https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-8.html#-top-level-await
Aus dem Beitrag:
Dies liegt daran, dass zuvor in Das Warten auf JavaScript (zusammen mit den meisten anderen Sprachen mit einer ähnlichen Funktion) war nur im Hauptteil einer asynchronen Funktion zulässig. Mit Warten auf oberster Ebene können wir jedoch Warten auf oberster Ebene eines Moduls verwenden.

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

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

Beachten Sie, dass es eine Subtilität gibt: Das Warten auf oberster Ebene funktioniert nur auf der obersten Ebene eines Moduls, und Dateien werden nur dann als Module betrachtet, wenn TypeScript einen Import oder Export findet. In einigen grundlegenden Fällen müssen Sie möglicherweise export {} als Boilerplate ausschreiben, um dies sicherzustellen.

Das Warten auf oberster Ebene funktioniert möglicherweise nicht in allen Umgebungen, in denen Sie dies zu diesem Zeitpunkt erwarten. Derzeit können Sie das Warten auf oberster Ebene nur verwenden, wenn die Ziel-Compiler-Option es2017 oder höher ist und das Modul esnext oder system ist. Die Unterstützung in mehreren Umgebungen und Bundlern ist möglicherweise eingeschränkt oder erfordert möglicherweise die Aktivierung der experimentellen Unterstützung.

AXT
quelle