wake-up-neo.com

Woher weiß man, ob eine Funktion asynchron ist?

Ich muss eine Funktion an eine andere Funktion übergeben und sie als Callback ausführen. Das Problem ist, dass diese Funktion manchmal asynchron ist, wie zum Beispiel:

async function() {
 // Some async actions
}

Ich möchte also await callback() oder callback() ausführen, abhängig von der Art der Funktion, die er empfängt.

Gibt es eine Möglichkeit, den Typ der Funktion zu kennen?

37
Facundo Matteo

Native async Funktionen können identifizierbar sein bei der Konvertierung in Strings :

asyncFn[Symbol.toStringTag] === 'AsyncFunction'

Oder mit dem Konstruktor AsyncFunction :

const AsyncFunction = (async () => {}).constructor;

asyncFn instanceof AsyncFunction === true

Dies funktioniert nicht mit Babel/TypeScript-Ausgaben, da asyncFn eine reguläre Funktion in transpiliertem Code ist, es sich um eine Instanz von Function oder GeneratorFunction handelt, nicht um AsyncFunction. Um sicherzustellen, dass es keine false positives für Generator- und reguläre Funktionen in transpiliertem Code gibt:

const AsyncFunction = (async () => {}).constructor;
const GeneratorFunction = (function* () => {}).constructor;

(asyncFn instanceof AsyncFunction && AsyncFunction !== Function && AsyncFunction !== GeneratorFunction) === true

Die Frage bezieht sich offensichtlich auf die Babel-Implementierung der Funktion async, die darauf beruht, dass transform-async-to-generator die Funktion async für Generatorfunktionen transpiliert Verwenden Sie auch transform-regenerator , um den Generator auf reguläre Funktionen umzustellen.

Das Ergebnis des Funktionsaufrufs async ist ein Versprechen. Gemäß dem Vorschlag kann ein Versprechen oder ein Nichtversprechen an await übergeben werden, sodass await callback() universell ist.

Es gibt nur wenige Edge-Fälle, in denen dies erforderlich sein kann. Beispielsweise verwenden native async -Funktionen native Versprechungen intern und greifen globale Promise nicht auf, wenn ihre Implementierung geändert wurde:

let NativePromise = Promise;
Promise = CustomPromiseImplementation;

Promise.resolve() instanceof Promise === true
(async () => {})() instanceof Promise === false;
(async () => {})() instanceof NativePromise === true;

Dies kann sich auf das Funktionsverhalten auswirken (dies ist ein bekanntes Problem bei der Implementierung von Angular und Zone.js ). Selbst dann ist es vorzuziehen, zu erkennen, dass der Funktionsrückgabewert nicht als Promise -Instanz erwartet wird, anstatt zu erkennen, dass eine Funktion async ist, da dasselbe Problem für alle Funktionen gilt, die eine alternative Versprechungsimplementierung verwenden, nicht Nur async ( Die Lösung für das Angular Problem ist, async Rückgabewert mit Promise.resolve).

TL; DR: async Funktionen sollten nicht von regulären Funktionen unterschieden werden, die Versprechungen zurückgeben. Sie sollten auf keinen Fall in a unterschieden werden Situation wie diese. Es gibt keinen zuverlässigen Weg und keinen Grund, nicht native transpilierte async -Funktionen zu erkennen.

44
Estus Flask

Sowohl @rnd als auch @estus sind korrekt.

Aber um die Frage mit einer tatsächlichen Arbeitslösung zu beantworten, gehen Sie hier

function isAsync (func) {
    const string = func.toString().trim();

    return !!(
        // native
        string.match(/^async /) ||
        // babel (this may change, but hey...)
        string.match(/return _ref[^\.]*\.apply/)
        // insert your other dirty transpiler check

        // there are other more complex situations that maybe require you to check the return line for a *promise*
    );
}

Dies ist eine sehr gültige Frage, und ich bin verärgert, dass ihn jemand abgestürzt hat. Die Hauptanwendung für diese Art der Überprüfung ist für Bibliotheken/Rahmen/Dekorateure.

Dies sind noch Anfänge, und wir sollten VALIDE Fragen nicht ablehnen.

13
Chad Scira

Ich bevorzuge diesen einfachen Weg:

theFunc.constructor.name == 'AsyncFunction'
8
Alexander

Falls Sie NodeJS 10.x oder höher verwenden

Verwenden Sie die native util-Funktion .

   util.types.isAsyncFunction(function foo() {});  // Returns false
   util.types.isAsyncFunction(async function foo() {});  // Returns true

Behalten Sie jedoch alle Bedenken von oben. Eine Funktion, die versehentlich ein Versprechen zurückgibt, liefert ein falsches Negativ.

Und dazu noch (aus den Dokumenten):

Beachten Sie, dass dies nur das zurückgibt, was die JavaScript-Engine sieht. Insbesondere kann der Rückgabewert nicht mit dem ursprünglichen Quellcode übereinstimmen, wenn ein Transpilationswerkzeug verwendet wurde.

Wenn Sie jedoch async in NodeJS 10 und keine Transiplation verwenden. Dies ist eine schöne Lösung.

4
Ian Segers

TL; DR

Kurze Antwort: Verwenden Sie instaceof nach ExposureAsyncFunction - siehe unten.

Lange Antwort: Tu das nicht - siehe unten.

Wie es geht

Sie können feststellen, ob eine Funktion mit dem Schlüsselwort async deklariert wurde

Wenn Sie eine Funktion erstellen, zeigt dies an, dass es sich um eine Typfunktion handelt:

> f1 = function () {};
[Function: f1]

Sie können es mit dem Operator instanceof testen:

> f1 instanceof Function
true

Wenn Sie eine Async-Funktion erstellen, zeigt dies an, dass es sich um einen Typ AsyncFunction handelt:

> f2 = async function () {}
[AsyncFunction: f2]

man könnte also erwarten, dass es auch mit instanceof getestet werden kann:

> f2 instanceof AsyncFunction
ReferenceError: AsyncFunction is not defined

Warum das? Weil die AsyncFunction kein globales Objekt ist. Siehe die Dokumente:

obwohl Sie, wie Sie sehen können, unter Reference/Global_Objects... aufgeführt ist.

Wenn Sie einen einfachen Zugriff auf die AsyncFunction benötigen, können Sie mein unexposed-Modul verwenden:

um eine lokale Variable zu erhalten:

const { AsyncFunction } = require('unexposed');

oder um eine globale AsyncFunction neben anderen globalen Objekten hinzuzufügen:

require('unexposed').addGlobals();

und jetzt funktioniert das wie erwartet:

> f2 = async function () {}
[AsyncFunction: f2]
> f2 instanceof AsyncFunction
true

Warum sollten Sie es nicht tun?

Der obige Code prüft, ob die Funktion mit dem Schlüsselwort async erstellt wurde. Beachten Sie jedoch, dass es nicht so wichtig ist, wie eine Funktion erstellt wurde, sondern ob eine Funktion ein Versprechen zurückgibt oder nicht.

Überall, wo Sie diese "async" -Funktion verwenden können:

const f1 = async () => {
  // ...
};

sie könnten auch folgendes verwenden:

const f2 = () => new Promise((resolve, reject) => {
});

es wurde zwar nicht mit dem Schlüsselwort async erstellt und wird daher nicht mit instanceof oder mit einer anderen in anderen Antworten angegebenen Methode abgeglichen.

Beachten Sie insbesondere Folgendes:

const f1 = async (x) => {
  // ...
};

const f2 = () => f1(123);

Der f2 ist nur f1 mit einem hartcodierten Argument und es macht keinen Sinn, async hier hinzuzufügen, obwohl das Ergebnis in jeder Hinsicht so viel "async" wie f1 ist.

Zusammenfassung

So ist es möglich zu prüfen, ob eine Funktion mit dem Schlüsselwort async erstellt wurde. Verwenden Sie sie jedoch mit Vorsicht, da Sie bei der Prüfung wahrscheinlich etwas falsch machen.

2
rsp

Es scheint, dass await auch für normale Funktionen verwendet werden kann. Ich bin nicht sicher, ob es sich um "gute Praxis" handelt, aber hier ist es

async function asyncFn() {
  // await for some async stuff
  return 'hello from asyncFn' 
}

function syncFn() {
  return 'hello from syncFn'
}

async function run() {
  console.log(await asyncFn()) // 'hello from asyncFn'
  console.log(await syncFn()) // 'hello from syncFn'
}

run()
1
gyo

Sie können zu Beginn davon ausgehen, dass Rückruf Versprechen ist:

export async function runSyncOrAsync(callback: Function) {

  let promisOrValue = callback()
  if (promisOrValue instanceof Promise) {
    promisOrValue = Promise.resolve(promisOrValue)
  }
  return promisOrValue;
}

und sie in Ihrem Code können Sie das tun:

await runSyncOrAsync(callback)

das löst dein Problem mit dem unbekannten Rückruftyp ....

0

Hei,

Hier ist ein Ansatz von David Walsh in seinem Blogpost :

const isAsync = myFunction.constructor.name === "AsyncFunction";

Prost!

0
theVoogie