TypeScript相当于Go语言的Defer语句

ovfsdjhp  于 2023-01-31  发布在  TypeScript
关注(0)|答案(3)|浏览(192)

TypeScript有没有类似Go语言的Defer语句?
我厌倦了在整个函数的多个位置编写清理代码。正在寻找一个更简单的解决方案。
我做了一个快速谷歌,但没有找到任何东西。

dhxwm5r4

dhxwm5r41#

答案可能是否定的,但您至少有两个选择:
1.正如@bereal提到的,你会用try/finally来做这个,关于try/finally,你在评论中说:
是的,除了我不想使用try catch块,因为它们可能很昂贵。
不完全是这样,抛出一个Error是很昂贵的(昂贵的是创建,填充堆栈信息;实际的“投掷”并不花费太多);输入try代码块就不是了,在JavaScript中,你不必抛出Error示例,你可以抛出任何东西如果你想抛出而不需要填充堆栈信息,你可以抛出一个非Error(尽管我不提倡).另外,finally块有轻微的开销,但我最近不得不在几个现代引擎中测量它,它确实是微不足道的。
1.你可以把一个函数赋给一个变量,然后在函数的末尾运行它(或者如果你想做几个的话,可以使用数组),对于单次清理,我预计它会比try/finally更昂贵,对于多次清理(否则需要嵌套的try/finally块),你必须找到答案。
FWIW,一些例子:
try/finally中执行单次清理:

function example() {
    try {
        console.log("hello");
    } finally {
        console.log("world");
    }
}
example();

try/finally中进行多次清理:

function example() {
    try {
        console.log("my");
        try {
            console.log("dog");
        } finally {
            console.log("has");
        }
    } finally {
        console.log("fleas");
    }
}
example();

通过分配功能进行单一清除:

function example() {
    let fn = null;
    fn = () => console.log("world");
    console.log("hello");
    if (fn) { // Allowing for the above to be conditional, even though
              // it isn't above
        fn();
    }
}
example();

try/finally中进行多次清理:

function example() {
    const cleanup = [];
    cleanup.push(() => console.log("has"));
    console.log("my");
    cleanup.push(() => console.log("fleas"));
    console.log("dog");
    cleanup.forEach(fn => fn());
}
example();

或按其他顺序:

function example() {
    const cleanup = [];
    cleanup.push(() => console.log("fleas"));
    console.log("my");
    cleanup.push(() => console.log("has"));
    console.log("dog");
    while (cleanup.length) {
        const fn = cleanup.pop();
        fn();
    }
}
example();
e0bqpujr

e0bqpujr2#

我想要的是一种更清晰的方法来完成defer的工作,就像在TypeScript中执行Go一样:

class Test {

  @Defer()
  async test () {
    const timer = setInterval(() => console.log('interval'), 1000)
    defer.call(this, () => clearInterval(timer))
    await new Promise(resolve => setTimeout(resolve, 1500))
  }

}

const t = new Test()
t.test()
  .catch(console.error)

在上面的代码中,我们定义了一个timer,用于每1秒输出一次interval,并定义了一个defer,用于在超出此函数作用域时清除间隔(与Go相同)。
运行时,await new Promise(resolve => setTimeout(resolve, 1500)将等待1.5秒,这将输出一行interval输出,然后程序退出。

$ ts-node src/defer.ts 
interval
$

下面的代码示例是完整的代码,可以使用TypeScript 4.4通过复制/粘贴直接运行这些代码:

const DEFER_SYMBOL = Symbol('defer')

type Callback = (err?: Error) => void

interface DeferizedThis {
  [DEFER_SYMBOL]?: Callback[],
}

function Defer () {
  return function callMethodDecorator (
    _target      : any,
    _propertyKey : string,
    descriptor   : PropertyDescriptor,
  ): PropertyDescriptor {

    const oldValue = descriptor.value

    async function deferizedMethod (
      this: DeferizedThis,
      ...args: any[]
    ) {
      try {
        const ret = await oldValue.apply(this, args)
        return ret
      } finally {
        if (this[DEFER_SYMBOL]) {
          const deferCallbacks = this[DEFER_SYMBOL]

          while (true) {
            const fn = deferCallbacks?.pop()
            if (!fn) { break }
            try { fn() } catch (e) { console.error(e) }
          }
        }
      }
    }

    descriptor.value = deferizedMethod
    return descriptor
  }
}

function defer (
  this: any,
  cb: Callback,
): void {
  if (this[DEFER_SYMBOL]) {
    this[DEFER_SYMBOL]!.push(cb)
  } else {
    this[DEFER_SYMBOL] = [cb]
  }
}

class Test {

  @Defer()
  async test () {
    const timer = setInterval(() => console.log('interval'), 1000)
    defer.call(this, () => clearInterval(timer))
    await new Promise(resolve => setTimeout(resolve, 1500))
  }

}

const t = new Test()
t.test()
  .catch(console.error)

如果您已经阅读了上面的代码,它显然是一个有缺陷的PoC,绝对不能在生产中使用。
我想讨论一下我们是否有什么好的方法可以在TypeScript中实现这一点,通过遵循这种装饰器的方式,或者其他任何方向。

xkftehaa

xkftehaa3#

也许这会有所帮助,从原来的T. J.克劳德(@t-j-克劳德)的React。
通过引用try/catch块的finally块中的变量延迟

function example() {
    const defers = [];
    try {
        var xx = "Firts";
        defers.push(((text) => () => console.log(text))(xx));
        xx = "No Firts";
        var yy = "Last";
        defers.push(((text) => () => console.log(text))(yy));
        yy = "No Last"
        const timer = setInterval(() => console.log('live'), 1000);
        defers.push(((t) => () => clearInterval(t))(timer));
    } finally {
        while (defers.length) {
            const fn = defers.pop();
            fn();
        }
    }
}
example();

相关问题