Handle async errors without nesting try / catch

Concept of async / await keywords made code much more readable and maintainable in comparison of callback hell but still we need to handle errors for async operations.

Usual try / catch blocks will make a code nested, create new closure making variables created inside inaccessible outside of try / catch.

To avoid new closure and make code flat (all rows are on the same level of depth) for async / await we can use a simple util calm

Solution goals

const [value, error] = await calm(fetch('https://api.my.app/endpoint'));
if (error || !value) {
  // handle case
  return;
}

operate(value);
usage sample

Implementation

type ThenArg<T> = T extends PromiseLike<infer U> ? U : T;

const calm = async <P extends Promise<any>>(p: P): Promise<[ThenArg<P> | undefined, Error | undefined]> => {
  try {
    const value = await p;
    return [value, undefined];
  } catch (error) {
    return [undefined, error];
  }
}
implementation details