Typescript 中的装饰器返回函数的时间

Posted

技术标签:

【中文标题】Typescript 中的装饰器返回函数的时间【英文标题】:Decorator in Typescript to return timing of function 【发布时间】:2021-09-23 18:20:27 【问题描述】:

用例:我想知道一个函数在打字稿中执行需要多少时间。我想为此目的使用装饰器。 我希望装饰器应该返回时间以便(我可以进一步使用它),而不仅仅是打印它。 例如:

export function createTimestamps(message: string) 
  return function (target: any, name: string, descriptor: PropertyDescriptor) 
    const method = descriptor.value;
    descriptor.value = async function () 
      const startTime = new Date(Date.now());
      console.log(
        `$message started at: $startTime.toLocaleString("en-GB")`
      );
      await method.apply(this);
      const endTime = new Date(Date.now());
      console.log(
        `$message completed at: $endTime.toLocaleString("en-GB")`
      );
      console.log(
        `$message took $
          endTime.getTime() - startTime.getTime()
        ms to complete.`
      );
    ;
  ;

如果我使用上面的函数作为装饰器,那么我希望装饰器返回 "endTime.getTime() - startTime.getTime()" ,以便我可以进一步使用它。

@creaTimestamp
async newfunc():string
 return "typescript";

现在,当我调用上述函数时,等待 newfunc()。我可以获取执行时间值以及它返回的字符串吗?

我还有很多函数我想避免在每个函数之上添加装饰器,因此在调用它们时我想确保装饰器运行并返回时间。如果存在这样的图书馆,有人可以指点我吗?

有人可以分享一些关于上述场景的见解吗,我对装饰器很陌生。谢谢!

【问题讨论】:

您能提供更多信息吗?您希望如何使用装饰器,您希望结果是什么?将一些示例作为代码发布,即使您知道它们不正确。这也可能接近你想要的? leefreeman.xyz/2020/05/08/typescript-decorators 感谢您的链接。所以在上面的链接中,我希望功能“计时器”与“价值”一起返回“运行时”?有可能吗? 嗯,你当然可以在装饰器中将方法的返回值替换为 [actualResult, time],但这会违反类型安全。我要做的是在装饰器中使用 Reflect.defineMetafata 并将最后执行时间存储在方法的元数据中。然后您可以稍后在代码中使用 Reflect.getMetadata 检索它。 @Papooch 你能举个例子吗? @simeon 我认为 Alex 举了一个很好的例子来说明我的想法。为了解决您对 我有许多功能我想避免在每个功能之上添加装饰器 的第二个问题,我会继续前进并无耻地插入我专门为此制作的 decorate-all 包目的。 【参考方案1】:

返回附加数据是不可能使用 typescript 装饰器的。

主要问题是a typescript decorator cannot change the return type的一个函数。而且一个函数只能返回一个值。

那么,为什么这很重要?

例如:

class Foo 
  async newfunc(): string 
    return "typescript";
  


const lang = await new Foo().newFunc()
console.log(`$lang is a cool language`)

在此示例中,langstring,您的程序期望它是 string。如果你把装饰器放在这个函数上,并且你期望返回一个字符串以及时间信息,你必须返回类似的东西:

 result: returnValueOfFunctionHere, elapsed: elapsedMsHere 

但这不再是string。现在您必须深入了解result 属性才能获取该字符串。这意味着您已经通过应用装饰器更改了函数的返回类型。目前是不允许的。

这就是示例将信息记录到控制台而不是返回它的原因。


但正如@Papooch 建议的那样,您可以通过reflect-metadata 将经过的时间存储在元数据 中。您可以将其存储在正在测量的函数的唯一元数据键上。

reflect-metadata 是一个有用的包,可以处理这样的事情。阅读更多关于how to use it here。

import 'reflect-metadata'

// Create a key to store the metadata under. This should be a symbol.
const lastCallElapsedKey = Symbol('lastCallElapsedKey')

function createTimestamps(message: string) 
  return function (target: any, name: string, descriptor: PropertyDescriptor) 
    const method = descriptor.value;

    descriptor.value = async function () 
      const startTime = new Date(Date.now());      
      const result = await method.apply(this);
      const endTime = new Date(Date.now());
      const elpased = endTime.getTime() - startTime.getTime()

      // Write the elpased time to the new function's metadata.
      Reflect.defineMetadata(lastCallElapsedKey, elpased, descriptor.value)
      
      return result
    ;
  ;


// Get the elapsed time from the metadata.
function getElapsed(fn: (...args: unknown[]) => unknown): number | undefined 
    return Reflect.getMetadata(lastCallElapsedKey, fn)


class Foo 
    // Emulate an async call
    @createTimestamps("test")
    async newfunc(): Promise<string> 
        return new Promise(resolve => 
            setTimeout(() => resolve('typescript'), 250)
        )
    


async function test() 
    const foo = new Foo()
    console.log(await foo.newfunc())
    console.log(getElapsed(foo.newfunc)) // 250

test()

Playground

【讨论】:

非常感谢! @simeon 很高兴我能帮上忙!我还只想提一下,您关于将装饰器应用于许多功能而不必在每个功能上声明它的问题完全值得它自己的问题。祝你好运:)

以上是关于Typescript 中的装饰器返回函数的时间的主要内容,如果未能解决你的问题,请参考以下文章

TypeScript React HOC 返回类型与装饰器兼容

TypeScript基础入门之装饰器(三)

Typescript中的装饰器原理

13python中的函数(闭包与装饰器)

typeScript函数篇

TypeScript 素描 - 装饰器