如何优雅地为数组中列出的即将 executed/invoked 的函数和方法提供 try-catch 功能?

How does one elegantly provide try-catch functionality to functions and methods which are listed within an array and are about to be executed/invoked?

我有这个数组:

const arr = [
  { 'Some text': this.f('a') },
  { 'Some other text': this.f('b') }
]

我为此编写了实验代码。

arr.forEach((entry) => {
  console.log(Object.entries(entry));
})

结果它执行了我的功能:

[[ 'Some text': 'result_f1' ]]
[[ 'Some other text': 'result_f2' ]]

我的函数自动执行了。

但是我认为这不是实现该目标的最安全方法。我想更明确地分别执行每个函数以及尝试捕获每个函数。

我想这样做的原因是将每个函数包装到一个单独的 try-catch 块中,因为在数组中尝试捕获会破坏代码的可读性

有什么实现方法吗?

这些函数不是“自动”执行的,而是因为您显式调用它们而执行的:

const arr = [
    { 'Some text': this.f('a') },
    //             ^^^^^^^^^^^−−−−−−−−−−−−−− here
    { 'Some other text': this.f('b') }
    //                   ^^^^^^^^^^^−−−−−−−− and here
]

上面的结果是一个包含两个对象的数组,其中第一个对象有一个名为 Some text 的 属性,其值为 result 调用了 this.f('a'),第二个对象有一个名为 Some other text 的 属性,其值为调用了 this.f('b') 结果

如果您想将这些调用延迟到稍后的某个时间,您需要将它们包装在函数中:

const arr = [
    { 'Some text': () => this.f('a') },
    //             ^^^^^^^^^^^^^^^^^−−−−−−−−−−−−−− wrapper function
    { 'Some other text': () => this.f('b') }
    //                   ^^^^^^^^^^^^^^^^^−−−−−−−− wrapper function
]

以后,你会这样称呼他们:

arr[0]["Some text"]();
arr[0]["Some other tex"]();

或类似。

如果你想稍后在 try/catch 块中调用它们,你可以这样做:

for (const obj of arr) {
    for (const fn of Object.values(obj)) {
        try {
            fn();
        } catch (e) {
            // ...
        }
    }
}

...或等同于 forEach:

arr.forEach(obj => {
    Object.values(obj).forEach(fn => {
        try {
            fn();
        } catch (e) {
            // ...
        }
    });
});

在您说的另一条评论中:

how would you replace the function with function result?

我怀疑你想要这样的东西:

const arr = [
    { 'Some text': () => this.f('a') },
    { 'Some other text': () => this.f('b') }
].map(obj => {
    return Object.fromEntries(Object.entries(obj).map(([key, value]) => {
        try {
            value = value();
        } catch (e) {
            // ...presumably do something with the fact it failed...
        }
        return [key, value];
    }));
});

上面的结果是 arr 具有与初始对象字面量相同的键的对象,但值是调用函数的结果(或您写入 [=24= 的任何内容) ] 在 catch 块中)。

参见:

定义数组 arr 时已调用函数 this.f。这就是圆括号的作用。要推迟调用,您可以将参数“绑定”到函数,而无需实际调用函数 this.f.bin(this, 'a') 而不是 this.f('a')。这会创建一种可以在稍后阶段调用的闭包。

forEach 中,您可以显式调用函数:

arr.forEach((entry) => {
  Object.entries(entry).forEach(([key, fun]) => {
    console.log(key, fun())
  })
})

话虽如此,我不确定为什么要这样做。

+++ 迟到的答案,但我们开始了...+++

对于像 OP 描述的任务或类似的任务,例如... ... one could rely on two method modifier abstractions like (以后使用)and/or afterFinally 两者都专门针对 try-catch 基于 functions/methods 的异常处理,它们本身没有实现故障安全。

上面提到的两个抽象(将)提供包装 function/method 的功能,其方式不仅提供 try-catch 但也考虑了异常处理。

像 OP 这样的用例可以像下面的示例代码一样直接实现...

const testDummy = {
  x: 'x',
  y: 'y',
  foo: function (a, b) { return [this.x, this.y, a, b].join(' :: '); },
  bar: function (a, b) { throw new Error(`exception raised with arguments a: ${ a } and b: ${ b }`) } ,
};
const testConfig = {
  'non failing method invocation': testDummy.foo.bind(testDummy, 'aa', 'bb'),
  'method invocation with raised exception': testDummy.bar.bind(testDummy, 'bazz', 'bizz'),
};

function handleException({ name, message, stack }/*, argsList */) {
  console.log(
    'handleException :: error ...', { name, message, stack }
  );
  return 'custom/sanitized return value for raised exception';
}
function createAndExecuteFailSafeTestAndReturnResult([description, executee]) {
  const test = executee.afterThrowing(handleException/*, target */);
  const result = test();

  return [[description, 'result ...'].join(' :: '), result];
}

console.log({
  testConfig,
  testResult: Object.fromEntries(
    Object
      .entries(testConfig)
      .map(createAndExecuteFailSafeTestAndReturnResult)
  )
});
.as-console-wrapper { min-height: 100%!important; top: 0; }
<script>
  (function (Function) {

    function isFunction(value) {
      return (
        typeof value === 'function' &&
        typeof value.call === 'function' &&
        typeof value.apply === 'function'
      );
    }
    function getSanitizedTarget(value) {
      return value ?? null;
    }

    function afterThrowing/*Modifier*/(handler, target) {
      target = getSanitizedTarget(target);

      const proceed = this;
      return (

        isFunction(handler) &&
        isFunction(proceed) &&

        function afterThrowingType(...argumentArray) {
          const context = getSanitizedTarget(this) ?? target;

          let result;
          try {
            // try the invocation of the original function.

            result = proceed.apply(context, argumentArray);

          } catch (exception) {

            result = handler.call(context, exception, argumentArray);
          }
          return result;
        }

      ) || proceed;
    }
    // afterThrowing.toString = () => 'afterThrowing() { [native code] }';

    Object.defineProperty(Function.prototype, 'afterThrowing', {
      configurable: true,
      writable: true,
      value: afterThrowing/*Modifier*/
    });

  }(Function));
</script>