4

These are some of my functions, I need to write a common function to see if functions are running without error. I tried with try/catch method. But I could only do that individually on each function.

function fisrt(){
 console.log("First");
};

function second(){
 console.log("Second");
}

function third(){
 console.log("Third");
}
fisrt();
second();
third();

I was writing each function inside the try-catch. Is there a way I can write a common try-catch for all the functions.

try {
     (function first() {
       console.log("ffgdf")
    })();

 }catch (e) {
      console.log( "won't work" );
    }
Peter Seliger
  • 11,747
  • 3
  • 28
  • 37
ANZR
  • 81
  • 1
  • 2
  • 8
  • 2
    The code you posted does not show any attempt of using try/catch. Please add this to the code. – Wais Kamal Jul 03 '18 at 16:04
  • If you call multiple functions within a try/catch block then that's where exceptions from any of the functions will be caught. – Alex K. Jul 03 '18 at 16:08

3 Answers3

6

You could define a wrapper function, that takes your desired function as a parameter, and wraps it in a try catch.

function wrapper(fn) {
    try {
        fn();
    } catch(error) {
        console.error(error);
    }
}

Then given your original functions:

function first() {
    console.log("First");
};

function second() {
    console.log("Second");
}

function third() {
    console.log("Third");
}

You can test each one by using your wrapper function:

wrapper(first);
wrapper(second);
wrapper(third);

Without needing to add try catch to each function.

Steve Vaughan
  • 2,163
  • 13
  • 18
  • I'm getting an error saying, fn() is not a function – ANZR Jul 03 '18 at 17:33
  • @ANZR what are you passing as the argument to wrapper()? – Steve Vaughan Jul 03 '18 at 18:13
  • Vaugha, All my functions were called inside the window.trigger function, how to implement from there. I have edited the code above. – ANZR Jul 03 '18 at 20:00
  • I'm passing the name of the function. – ANZR Jul 03 '18 at 20:19
  • I don't see any edit code above. But if you're passing these as a callback to a window.trigger event. You will want to do something like this: `window.trigger('eventname', function() { wrapper(first); })` – Steve Vaughan Jul 03 '18 at 20:23
  • Its a great answer, was first thing came to my mind. But its not perfect yet. Is there any way to automatically catch the method call of a service and then wrap it to try/catch? – Abror Abdullaev Jun 16 '23 at 12:32
1

Regarding the introducing sentence of the accepted answer two years ago ...

You could define a wrapper function, that takes your desired function as a parameter, and wraps it in a try catch.

... this part could be covered with (an) abstraction(s) where one also can provide the (different) handling of an invocation failure (the catch and exception clause).

If one does, for instance, provide functionality that wraps a function/method in way that does not only provide the try catch but also takes the exception handling into account, one can easily accomplish tasks, like the one asked by the OP for, which are mainly about programmable approaches for automatically creating and processing lists of functions/methods that are going to be invoked whilst suppressing unintended/unexpected invocation failures.

The following example does implement afterThrowing and afterFinally, two method modifier methods, each processing the original function/method with an exception handler as its first argument.

Making use of the provided abstraction(s) the approach itself boils down to writing reduce functionality which processes an array of functions by invoking each function with its own set of arguments and collecting its invocation success state ...

function first(...args) {
  console.log("first :: does succeed :: argsList :", args);
  return args.join(', ');
}
function second(...args) {
  console.log("second :: going to fail :: argsList :", args);
  throw new Error('2nd invocation failed.');
}
function third(...args) {
  console.log("third :: going to fail :: argsList :", args);
  throw new Error('3rd invocation failed.');
}
function fourth(...args) {
  console.log("fourth :: does succeed :: argsList :", args);
  return args.join(', ');
}
function fifth(...args) {
  console.log("fifth :: does succeed :: argsList :", args);
  return args.join(', ');
}


/**
 *  reduce functionality which processes an array of functions.
 */
function collectResultAfterThrowing(collector, fct, idx) {
  function afterThrowingHandler(error, argsArray) {
    // - can access the try-catch exception and the arguments
    //   that have been passed prior to the invocation failure.
    return {
      success: false,
      failure: {
        message: error.toString(),
        argsList: Array.from(argsArray)
      }
    }
  }
  function unifyResult(value) {
    return ((
         value
      && value.hasOwnProperty('success')
      && (value.success === false)
      && value
    ) || { success: true, value });
  }
  collector.results.push(
    unifyResult(fct                         // - modify original function towards an
      .afterThrowing(afterThrowingHandler)  //   `afterThrowing` handling of its try-catch result(s).
      .apply(null, collector.listOfArguments[idx])
    )
    // - an `afterThrowing` modified function does always return either the result of the
    //   original function's invocation or the return value of its 'afterThrowing' handler.
  );
  return collector;
}


/**
 *  reduce functionality which processes an array of functions.
 */
function collectResultAfterFinally(collector, fct, idx) {
  function isError(type) {
    return (/^\[object\s+Error\]$/).test(Object.prototype.toString.call(type));
  }
  function createResult(value) {
    return (isError(value) && {
      success: false,
      message: value.toString()
    } || {
      success: true,
      value
    });
  }
  collector.results.push(
    createResult(fct            // - modify original function towards an
      .afterFinally(() => null) //   `afterFinally` handling of its try-catch result(s).
      .apply(null, collector.listOfArguments[idx])
    )
    // - an `afterFinally` modified function does always return either the result of the
    //   original function's invocation or the try-catch exception of the invocation attempt.
  );
  return collector;
}


// ... two times, each the actual task, 
// ... once based on "afterThrowing" and
// ... once based on "afterFinally" ...

console.log('"afterThrowing" based try-and-catch results :', [

    first,
    second,
    third,
    fourth,
    fifth

  ].reduce(collectResultAfterThrowing, {

    listOfArguments: [
      ['foo', 'bar'],
      ['baz', 'biz'],
      ['buz', 'foo'],
      ['bar', 'baz'],
      ['biz', 'buz']
    ],
    results: []

  }).results
);
console.log('\n\n\n');

console.log('"afterFinally" based try-and-catch results :', [

    first,
    second,
    third,
    fourth,
    fifth

  ].reduce(collectResultAfterFinally, {

    listOfArguments: [
      ['foo', 'bar'],
      ['baz', 'biz'],
      ['buz', 'foo'],
      ['bar', 'baz'],
      ['biz', 'buz']
    ],
    results: []

  }).results
);
.as-console-wrapper { min-height: 100%!important; top: 0; }
<script>
  (function (Function) {

    const fctPrototype = Function.prototype;
    const FUNCTION_TYPE = (typeof Function);

    function isFunction(type) {
      return (
           (typeof type == FUNCTION_TYPE)
        && (typeof type.call == FUNCTION_TYPE)
        && (typeof type.apply == FUNCTION_TYPE)
      );
    }
    function getSanitizedTarget(target) {
      return ((target != null) && target) || null;
    }

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

      const proceed = this;
      return (

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

        function () {
          const context = target || getSanitizedTarget(this);
          const args = arguments;

          let result;
          try {
            result = proceed.apply(context, args);

          } catch (exception) {

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

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

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

      const proceed = this;
      return (

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

        function () {
          const context = target || getSanitizedTarget(this);
          const args = arguments;

          let result, error;
          try {
            result = proceed.apply(context, args);

          } catch (exception) {

            error = exception;

          } // finally { ... }

          result = (error || result);
          handler.call(context, result, args);

          return result;
        }

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

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

    Object.defineProperty(fctPrototype, 'afterFinally', {
      configurable: true,
      writable: true,
      value: afterFinally/*Modifier*/
    });

  }(Function));
</script>
Peter Seliger
  • 11,747
  • 3
  • 28
  • 37
  • Hi, when would one use afterThrowing vs afterFinally? They appear to be two different methods returning essentially the same results. Thanks! – cmill Oct 26 '21 at 14:42
  • 1
    @cmill ... 1/3 ... One would use [`afterThrowing`](https://github.com/petsel/es-function-modifiers/blob/main/src/modifiers/afterThrowing/index.js#L31) exclusively for exception handling since the provided `handler` will be executed only in case of a failing invocation of the original function. The handler gets passed the `error` or `reason` and the arguments array used in the original function's invocation. The modified function always returns either of the following results, the return value of the successfully invoked original function or the return value of the provided exception handler. – Peter Seliger Oct 26 '21 at 15:45
  • 1
    @cmill ... 2/3 ... in comparison to `afterThrowing` where the handler gets invoked only in case of a failure and the return value is the result of either the original function or the exception handler, [`afterFinally`](https://github.com/petsel/es-function-modifiers/blob/main/src/modifiers/afterFinally/index.js#L31) always does invoke the provided handler, regardless of a failing/non failing execution of the original function (hence finally). The value which gets passed to the handler is also the modified function's return value and either is the non failing result or the the `error`/`reason`. – Peter Seliger Oct 26 '21 at 15:45
  • 1
    @cmill ... 3/3 ... In comparison to the [`after`](https://github.com/petsel/es-function-modifiers/blob/main/src/modifiers/after/index.js#L23) modifier all three have in common that a modified function/method call without raised exception will return the result of the original function, whereas only the former two implement exception handling each. Thus, the use case for `afterFinally` is more of a generic introspection task for a non-/failing operation but `afterThrowing` is better suited for a specific exception handling and for tailored result cosmetics/sanitizing. – Peter Seliger Oct 26 '21 at 17:34
  • 1
    @PeterSmith I really appreciate the clarification. It is extremely helpful when someone explains in detail as you have! Clean code examples like this help those of us at an intermediate level benefit from seeing how an experienced developer would handle a problem. I learned a lot in the process of studying this today. I appreciate your time and explanation. Cheers! – cmill Oct 26 '21 at 18:31
1

In case of you wishing to get a "throwed" exception, you can use the Promise.all.

function parent() {
    function first() {
        console.log('First');
        throw new Error('First error');
    }
    
    function second() {
        console.log('Second');
    }
    
    function third() {
        console.log('Third');
    }
    
    return Promise.all([
        first(),
        second(),
        third(),
    ])
}

parent()
     .then(result => console.log(result))
     .catch(error => console.error(error))