如何知道一个函数是否是异步的? [英] How to know if a function is async?

查看:52
本文介绍了如何知道一个函数是否是异步的?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我必须将一个函数传递给另一个函数,并将其作为回调执行.问题是有时这个函数是异步的,比如:

异步函数() {//一些异步操作}

所以我想根据它接收的函数类型执行 await callback()callback().

有没有办法知道函数的类型??

解决方案

理论

原生 async 函数可能是可识别的 转换为字符串时:

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

或者通过 AsyncFunction 构造函数:

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

这不适用于 Babel/TypeScript 输出,因为 asyncFn 是转译代码中的常规函数​​,它是 FunctionGeneratorFunction,而不是 AsyncFunction.确保它不会为转译代码中的生成器和常规函数提供误报:

const AsyncFunction = (async() => {}).constructor;const GeneratorFunction = (function* () => {}).constructor;(asyncFn instanceof AsyncFunction && AsyncFunction !== Function && AsyncFunction !== GeneratorFunction) === true

由于原生 async 函数是在 2017 年正式引入 Node.js 的,所以问题很可能指的是 async 函数的 Babel 实现,它依赖于 transform-async-to-generator 转译 异步到生成器函数,也可以使用transform-regenerator 将生成器转换为常规函数.

async 函数调用的结果是一个承诺.根据提案,可以通过promise或non-promiseawait,所以 await callback() 是通用的.

可能需要这样做的边缘情况很少.例如,本机 async 函数在内部使用本机承诺,并且如果其实现发生更改,则不会选择全局 Promise:

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

这可能会影响函数行为(这是 Angular 和 Zone.js 承诺实现的已知问题).即便如此,最好还是检测函数返回值不是预期的 Promise 实例,而不是检测函数是 async,因为同样的问题适用于任何使用替代的函数promise 实现,而不仅仅是 async(上述Angular问题的解决方案是用Promise.resolve包裹async返回值.>

练习

从外面看,async 函数只是一个无条件返回原生promise 的函数,因此它应该被视为一个函数.即使一个函数曾经被定义为async,它也可以在某个时候被转译并成为常规函数.

一个可以返回promise的函数

在 ES6 中,一个可能返回 promise 的函数可以与 Promise.resolve(允许同步错误)或包装的 Promise 构造函数(处理同步错误)一起使用:

Promise.resolve(fnThatPossivelyReturnsAPromise()).then(result => ...);新承诺(解决 => 解决(fnThatPossivelyReturnsAPromiseOrThrows())).then(result => ...);

在 ES2017 中,这是通过 await 完成的(这就是问题示例的编写方式):

let result = await fnThatPossivelyReturnsAPromiseOrThrows();...

应该返回承诺的函数

检查一个对象是否是一个承诺是a一个单独的问题,但通常它不应该太严格或松散以涵盖极端情况.如果全局 Promise 被替换,instanceof Promise 可能不起作用,Promise !== (async () => {})().constructor.当 Angular 和非 Angular 应用程序交互时,可能会发生这种情况.

一个需要async的函数,即总是返回一个promise应该首先被调用,然后返回值被检查为一个promise:

let promise = fnThatShouldReturnAPromise();if (promise && typeof promise.then === 'function' && promise[Symbol.toStringTag] === 'Promise') {//是符合本机承诺的实现} 别的 {throw new Error('需要异步函数');}

TL;DR:async 函数不应与返回承诺的常规函数​​区分开来.没有可靠的方法和实际理由来检测非本地转译的 async 函数.

I have to pass a function to another function, and execute it as a callback. The problem is that sometimes this function is async, like:

async function() {
 // Some async actions
}

So I want to execute await callback() or callback() depending on the type of function that it is receiving.

Is there a way to know the type of the function??

解决方案

Theory

Native async functions may be identifiable when being converted to strings:

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

Or by AsyncFunction constructor:

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

asyncFn instanceof AsyncFunction === true

This won't work with Babel/TypeScript output, because asyncFn is regular function in transpiled code, it is an instance of Function or GeneratorFunction, not AsyncFunction. To make sure that it won't give false positives for generator and regular functions in transpiled code:

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

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

Since native async functions were officially introduced to Node.js in 2017, the question likely refers to Babel implementation of async function, which relies on transform-async-to-generator to transpile async to generator functions, may also use transform-regenerator to transpile generator to regular functions.

The result of async function call is a promise. According to the proposal, a promise or a non-promise may be passed to await, so await callback() is universal.

There are only few edge cases when this may be needed. For instance, native async functions use native promises internally and don't pick up global Promise if its implementation was changed:

let NativePromise = Promise;
Promise = CustomPromiseImplementation;

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

This may affect function behaviour (this is a known problem for Angular and Zone.js promise implementation). Even then it's preferable to detect that function return value is not expected Promise instance instead of detecting that a function is async, because the same problem is applicable to any function that uses alternative promise implementation, not just async (the solution to said Angular problem is to wrap async return value with Promise.resolve).

Practice

From the outside, async function is just a function that unconditionally returns native promise, therefore it should be treated like one. Even if a function once was defined async, it can be transpiled at some point and become regular function.

A function that can return a promise

In ES6, a function that potentially returns a promise can be used with Promise.resolve (lets synchronous errors) or wrapped Promise constructor (handles synchronous errors):

Promise.resolve(fnThatPossiblyReturnsAPromise())
.then(result => ...);

new Promise(resolve => resolve(fnThatPossiblyReturnsAPromiseOrThrows()))
.then(result => ...);

In ES2017, this is done with await (this is how the example from the question is supposed to be written):

let result = await fnThatPossiblyReturnsAPromiseOrThrows();
...

A function that should return a promise

Checking if an object is a promise is a matter of a separate question, but generally it shouldn't be too strict or loose in order to cover corner cases. instanceof Promise may not work if global Promise was replaced, Promise !== (async () => {})().constructor. This can happen when Angular and non-Angular applications interface.

A function that requires to be async, i.e. to always return a promise should be called first, then returned value is checked to be a promise:

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 functions shouldn't be distinguished from regular functions that return promises. There is no reliable way and no practical reason to detect non-native transpiled async functions.

这篇关于如何知道一个函数是否是异步的?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆