Ich muss eine Funktion an eine andere Funktion übergeben und als Rückruf ausführen. Das Problem ist, dass diese Funktion manchmal asynchron ist, wie:
async function() {
// Some async actions
}
Ich möchte also ausführen await callback()
oder callback()
abhängig von der Art der Funktion, die es empfängt.
Gibt es eine Möglichkeit, den Typ der Funktion zu kennen?
javascript
node.js
async-await
ecmascript-next
Facundo Matteo
quelle
quelle
await
ein Nicht-Versprechen haben, wird es trotzdem automatisch verpackt)Antworten:
Theorie
Native
async
Funktionen können bei der Konvertierung in Zeichenfolgen identifiziert werden :asyncFn[Symbol.toStringTag] === 'AsyncFunction'
Oder vom
AsyncFunction
Konstruktor:const AsyncFunction = (async () => {}).constructor; asyncFn instanceof AsyncFunction === true
Dies funktioniert nicht mit der Babel / TypeScript-Ausgabe, da
asyncFn
es sich bei der regulären Funktion im transpilierten Code um eine Instanz vonFunction
oderGeneratorFunction
nicht handeltAsyncFunction
. Um sicherzustellen, dass im transpilierten Code keine Fehlalarme für Generator- und reguläre Funktionen ausgegeben werden:const AsyncFunction = (async () => {}).constructor; const GeneratorFunction = (function* () => {}).constructor; (asyncFn instanceof AsyncFunction && AsyncFunction !== Function && AsyncFunction !== GeneratorFunction) === true
Da native
async
Funktionen 2017 offiziell in Node.js eingeführt wurden, bezieht sich die Frage wahrscheinlich auf die Babel-Implementierung vonasync
Funktionen, die sich auftransform-async-to-generator
das Transpilierenasync
in Generatorfunktionen stützen und möglicherweise auchtransform-regenerator
zum Transpilieren von Generatoren in reguläre Funktionen verwendet werden.Das Ergebnis des
async
Funktionsaufrufs ist ein Versprechen. Dem Vorschlag zufolge kann ein Versprechen oder ein Nichtversprechen weitergegeben werdenawait
,await callback()
was universell ist.Es gibt nur wenige Randfälle, in denen dies erforderlich sein kann. Zum Beispiel verwenden native
async
Funktionen native Versprechen intern und greifen nicht global auf,Promise
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 das Funktionsverhalten beeinflussen (dies ist ein bekanntes Problem für die Implementierung von Angular und Zone.js Versprechen ). Selbst dann ist es vorzuziehen, zu erkennen, dass der Funktionsrückgabewert nicht als
Promise
Instanz erwartet wird, anstatt zu erkennen, dass es sich um eine Funktion handeltasync
, da dasselbe Problem auf jede Funktion anwendbar ist, die eine alternative Versprechenimplementierung verwendet, und nicht nurasync
( die Lösung für dieses Winkelproblem besteht darin, dieasync
Rückgabe zu verpacken Wert mitPromise.resolve
).Trainieren
Von außen ist
async
Funktion nur eine Funktion, die bedingungslos native Versprechen zurückgibt, daher sollte sie wie eine behandelt werden. Selbst wenn eine Funktion einmal definiert wurdeasync
, kann sie irgendwann transpiliert werden und zu einer regulären Funktion werden.Eine Funktion, die ein Versprechen zurückgeben kann
In ES6 kann eine Funktion, die möglicherweise ein Versprechen zurückgibt, mit
Promise.resolve
(lässt synchrone Fehler) oder einem umschlossenenPromise
Konstruktor (behandelt synchrone Fehler) verwendet werden:Promise.resolve(fnThatPossiblyReturnsAPromise()) .then(result => ...); new Promise(resolve => resolve(fnThatPossiblyReturnsAPromiseOrThrows())) .then(result => ...);
In ES2017 geschieht dies mit
await
(so soll das Beispiel aus der Frage geschrieben werden):let result = await fnThatPossiblyReturnsAPromiseOrThrows(); ...
Eine Funktion, die ein Versprechen zurückgeben sollte
Die Überprüfung, ob ein Objekt ein Versprechen ist, ist eine separate Frage , sollte jedoch im Allgemeinen nicht zu streng oder locker sein, um Eckfälle abzudecken.
instanceof Promise
funktioniert möglicherweise nicht, wenn globalPromise
ersetzt wurdePromise !== (async () => {})().constructor
. Dies kann passieren, wenn Angular- und Nicht-Angular-Anwendungen eine Schnittstelle bilden.Eine Funktion, die erforderlich ist
async
, dh immer ein Versprechen zurückzugeben, sollte zuerst aufgerufen werden. Anschließend wird der zurückgegebene Wert als Versprechen überprüft:let promise = fnThatShouldReturnAPromise(); if (promise && typeof promise.then === 'function' && promise[Symbol.toStringTag] === 'Promise') { // is compliant native promise implementation } else { throw new Error('async function expected'); }
TL; DR:
async
Funktionen sollten nicht von regulären Funktionen unterschieden werden, die Versprechen zurückgeben. Es gibt keinen zuverlässigen Weg und keinen praktischen Grund, nicht native transpilierteasync
Funktionen zu erkennen .quelle
AsyncFunction !== Function
ist immer falsch, obwohl ich Funktionen mit einem Schlüsselwort habe,async
das als Argument an eineit()
Spezifikation übergeben wird. Ich benutze übrigens Typescript. Könnten Sie sich bitte diese Frage ansehen und Ihre Erkenntnisse liefern. Ich habe so viele verschiedene Wege ausprobiert, aber es ist mir noch nicht gelungen. :(AsyncFunction !== Function
, dass die Überprüfung dazu dient, Fehlalarme zu vermeiden . Im transpilierten Code gibt es keine echten Positiven , da sich dieasync
Funktionen nicht von den regulären im transpilierten Code unterscheiden.await
, er funktioniert für Versprechen und Nichtversprechen. Dies zeigt der letzte Ausschnitt in der Antwort.Ich bevorzuge diesen einfachen Weg:
theFunc.constructor.name == 'AsyncFunction'
quelle
theFunc = new class AsyncFunction extends Function {}
. Aber transpiledasync
Funktion nicht ,theFunc = () => __awaiter(void 0, void 0, void 0, function* () { })
.=== 'AsyncFunction'
wie von @theVoogie vorgeschlagen verwenden?Sowohl @rnd als auch @estus sind korrekt.
Aber um die Frage mit einer tatsächlich funktionierenden Lösung zu beantworten, gehen Sie
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 berechtigte Frage, und ich bin verärgert, dass ihn jemand abgelehnt hat. Der Hauptanwendungsfall für diese Art der Überprüfung ist eine Bibliothek / ein Framework / ein Dekorateur.
Dies sind frühe Tage, und wir sollten GÜLTIGE Fragen nicht ablehnen .
quelle
_ref
sind nicht da.fn.constructor.name
dieAsyncFunction
für asynchrone Funktionen.await
Semantik. Es spielt keine Rolle, ob sich eine Funktionasync
in einem mir bekannten praktischen Szenario befindet.async
ist nur eine Funktion, die bedingungslos native Versprechen zurückgibt - und wie eine behandelt werden sollte.async
kann irgendwann transpiliert werden, dies sollte eine App nicht ruinieren. Für das von Ihnen beschriebene Szenario ist es richtig, dass ein Wrapper eine Funktion aufruft und einen Wert als vielversprechend und nicht als funktionierend festlegtasync
. Wenn ungültige Handler so schnell wie möglich verhindert werden sollen, muss dies zur Entwurfszeit mit TS / FlowFalls 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
Beachten Sie jedoch alle Bedenken der oben genannten Antworten. Eine Funktion, die nur versehentlich ein Versprechen zurückgibt, gibt ein falsches Negativ zurück.
Und obendrein (aus den Dokumenten):
Aber wenn Sie
async
in NodeJS 10 und keine Transiplation verwenden. Dies ist eine schöne Lösung.quelle
TL; DR
Kurze Antwort:
instaceof
Nach dem Belichten verwendenAsyncFunction
- siehe unten.Lange Antwort: Tu das nicht - siehe unten.
Wie es geht
Sie können feststellen, ob eine Funktion mit dem
async
Schlüsselwort deklariert wurdeWenn Sie eine Funktion erstellen, wird angezeigt, dass es sich um eine Funktion handelt:
> f1 = function () {}; [Function: f1]
Sie können es mit dem
instanceof
Bediener testen :> f1 instanceof Function true
Wenn Sie eine asynchrone Funktion erstellen, wird angezeigt, dass es sich um eine AsyncFunction handelt:
> f2 = async function () {} [AsyncFunction: f2]
man könnte also erwarten, dass es auch getestet werden kann mit
instanceof
:> f2 instanceof AsyncFunction ReferenceError: AsyncFunction is not defined
Warum das? Weil die AsyncFunction kein globales Objekt ist. Siehe die Dokumente:
obwohl, wie Sie sehen können, es unter
Reference/Global_Objects
...Wenn Sie einen einfachen Zugang zum benötigen
AsyncFunction
, können Sie meinunexposed
Modul verwenden:um entweder eine lokale Variable zu erhalten:
const { AsyncFunction } = require('unexposed');
oder um ein globales
AsyncFunction
neben anderen globalen Objekten hinzuzufügen :require('unexposed').addGlobals();
und jetzt funktioniert das oben genannte wie erwartet:
> f2 = async function () {} [AsyncFunction: f2] > f2 instanceof AsyncFunction true
Warum solltest du es nicht tun?
Der obige Code testet, ob die Funktion mit dem
async
Schlüsselwort erstellt wurde. Beachten Sie jedoch, dass es nicht darauf ankommt, wie eine Funktion erstellt wurde, sondern ob eine Funktion ein Versprechen zurückgibt.Überall, wo Sie diese "asynchrone" Funktion verwenden können:
const f1 = async () => { // ... };
Sie können dies auch verwenden:
const f2 = () => new Promise((resolve, reject) => { });
obwohl es nicht mit dem
async
Schlüsselwort erstellt wurde und daher nicht mitinstanceof
oder mit einer anderen Methode übereinstimmt , die in anderen Antworten veröffentlicht wurde .Beachten Sie insbesondere Folgendes:
const f1 = async (x) => { // ... }; const f2 = () => f1(123);
Das
f2
ist nurf1
mit fest codierten Argumenten und es macht nicht viel Sinn,async
hier hinzuzufügen , obwohl das Ergebnis genauso "asynchron" sein wird wief1
in jeder Hinsicht.Zusammenfassung
Es ist also möglich zu überprüfen, ob eine Funktion mit dem
async
Schlüsselwort erstellt wurde, aber verwenden Sie sie mit Vorsicht, da Sie beim Überprüfen höchstwahrscheinlich etwas falsch machen.quelle
async
zu wissen, ob sie eine asynchrone / wartende Operation im Inneren ausführt, aber nichts zurückgibt.then().catch()
asynchrone Funktion,try/await
sondern diese. Also ja, Sie sollten unbedingt den Typ der Funktion überprüfen, wenn Sie unbedingt wissen möchten, ob sie asynchron ist oder nicht, aber nicht mitinstanceof
: Verwenden Siefn.constructor.name
stattdessen. Wenn dies nicht derAsyncFunction
Fall istFunction
, wissen Sie, dass es sich um eine asynchrone Funktion handelt.Es scheint, dass
await
dies auch für normale Funktionen verwendet werden kann. Ich bin nicht sicher, ob es als "gute Praxis" angesehen werden kann, 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()
quelle
Hei,
Hier ist ein Ansatz von David Walsh in seinem Blogpost :
const isAsync = myFunction.constructor.name === "AsyncFunction";
Prost!
quelle
Sie können zu Beginn davon ausgehen, dass ein Rückruf vielversprechend 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 dies tun:
await runSyncOrAsync(callback)
das wird Ihr Problem mit unwissendem Rückruftyp lösen ....
quelle