secuencialmente - orden de ejecucion de funciones javascript
¿Cómo saber si una función es asíncrona? (5)
TL; DR
Respuesta corta: use instaceof
después de exposing AsyncFunction
- vea más abajo.
Respuesta larga: no hagas eso, mira abajo.
Cómo hacerlo
Puede detectar si una función fue declarada con la palabra clave async
Cuando creas una función, muestra que es un tipo de función:
> f1 = function () {};
[Function: f1]
Puedes probarlo con el operador instanceof
:
> f1 instanceof Function
true
Cuando creas una función asíncrona, muestra que es un tipo AsyncFunction:
> f2 = async function () {}
[AsyncFunction: f2]
así que uno podría esperar que también se pueda probar con instanceof
:
> f2 instanceof AsyncFunction
ReferenceError: AsyncFunction is not defined
¿Porqué es eso? Porque el AsyncFunction no es un objeto global. Ver los documentos:
aunque, como puede ver, está listado en Reference/Global_Objects
...
Si necesita un acceso fácil a AsyncFunction
, puede usar mi módulo unexposed
:
para obtener una variable local:
const { AsyncFunction } = require(''unexposed'');
o para agregar un AsyncFunction
global junto con otros objetos globales:
require(''unexposed'').addGlobals();
y ahora lo anterior funciona como se espera:
> f2 = async function () {}
[AsyncFunction: f2]
> f2 instanceof AsyncFunction
true
¿Por qué no deberías hacerlo?
El código anterior comprobará si la función se creó con la palabra clave async
pero tenga en cuenta que lo realmente importante no es cómo se creó una función, sino si una función devuelve o no una promesa.
En cualquier lugar donde pueda usar esta función "asíncrona":
const f1 = async () => {
// ...
};
También podrías usar esto:
const f2 = () => new Promise((resolve, reject) => {
});
aunque no se haya creado con la palabra clave async
y, por lo tanto, no se comparará con instanceof
ni con ningún otro método publicado en otras respuestas .
Específicamente, considera esto:
const f1 = async (x) => {
// ...
};
const f2 = () => f1(123);
El f2
es solo f1
con un argumento codificado y no tiene mucho sentido agregar async
aquí, aunque el resultado será tanto "async" como f1
en todos los aspectos.
Resumen
Por lo tanto, es posible verificar si una función se creó con la palabra clave async
, pero usarla con precaución porque cuando la marca es muy probable que esté haciendo algo mal.
Tengo que pasar una función a otra función y ejecutarla como devolución de llamada. El problema es que a veces esta función es asíncrona, como:
async function() {
// Some async actions
}
Así que quiero ejecutar a la await callback()
o callback()
dependiendo del tipo de función que está recibiendo.
¿Hay alguna forma de saber el tipo de función?
Las funciones async
nativas pueden ser identificables cuando se convierten a cadenas :
asyncFn[Symbol.toStringTag] === ''AsyncFunction''
O por el constructor AsyncFunction
:
const AsyncFunction = (async () => {}).constructor;
asyncFn instanceof AsyncFunction === true
Esto no funcionará con la salida de Babel / TypeScript, ya que asyncFn
es una función normal en código transpilado, es una instancia de Function
o GeneratorFunction
, no de AsyncFunction
. Para asegurarse de que no dará falsos positivos para el generador y las funciones regulares en el código transpilado:
const AsyncFunction = (async () => {}).constructor;
const GeneratorFunction = (function* () => {}).constructor;
(asyncFn instanceof AsyncFunction && AsyncFunction !== Function && AsyncFunction !== GeneratorFunction) === true
La pregunta obviamente se refiere a la implementación de Babel de la función async
, que se basa en la transform-async-to-generator
para trasladar de forma async
a las funciones del generador, también puede usar transform-regenerator
para transpilar el generador a funciones regulares.
El resultado de la llamada a la función async
es una promesa. De acuerdo con la propuesta , una promesa o no promesa puede pasarse a la await
, por lo que await callback()
es universal.
Solo hay unos pocos casos de borde cuando esto puede ser necesario. Por ejemplo, las funciones async
nativas usan promesas nativas internamente y no toman la Promise
global si se cambió su implementación:
let NativePromise = Promise;
Promise = CustomPromiseImplementation;
Promise.resolve() instanceof Promise === true
(async () => {})() instanceof Promise === false;
(async () => {})() instanceof NativePromise === true;
Esto puede afectar el comportamiento de la función (este es un problema conocido para la implementación de promesa de Angular y Zone.js ). Incluso entonces es preferible detectar que no se espera que la función devuelva valor. La instancia de Promise
lugar de detectar que una función es async
, porque el mismo problema es aplicable a cualquier función que use una implementación de promesa alternativa, no solo async
( la solución a dicho problema Angular es para envolver el valor de retorno async
con Promise.resolve
).
TL; DR: las funciones async
no deben distinguirse de las funciones regulares que devuelven promesas. Ciertamente no deberían distinguirse en una situación como esta. No existe una forma confiable ni razón para detectar funciones async
no nativas transpiladas.
Parece que await
puede usarse también para funciones normales. No estoy seguro de si puede considerarse una "buena práctica" pero aquí está:
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()
Prefiero esta manera simple:
theFunc.constructor.name == ''AsyncFunction''
Tanto @rnd como @estus son correctos.
Pero para responder a la pregunta con una solución de trabajo real aquí tienes
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*
);
}
Esta es una pregunta muy válida, y estoy molesto de que alguien rechazado lo haya votado. El caso de uso principal para este tipo de verificación es para una biblioteca / marco / decoradores.
Estos son los primeros días, y no debemos rechazar las preguntas VÁLIDAS .