rubico
Version:
[a]synchronous functional programming
99 lines (93 loc) • 3.33 kB
JavaScript
const isPromise = require('./_internal/isPromise')
const promiseAll = require('./_internal/promiseAll')
const __ = require('./_internal/placeholder')
const curry3 = require('./_internal/curry3')
const catcherApply = require('./_internal/catcherApply')
const areAnyValuesPromises = require('./_internal/areAnyValuesPromises')
// _tryCatch(tryer function, catcher function, args Array) -> Promise
const _tryCatch = function (tryer, catcher, args) {
try {
const result = tryer(...args)
return isPromise(result)
? result.catch(curry3(catcherApply, catcher, __, args))
: result
} catch (error) {
return catcher(error, ...args)
}
}
/**
* @name tryCatch
*
* @synopsis
* ```coffeescript [specscript]
* tryCatch(tryer function, catcher function)(...args) -> Promise|any
*
* tryCatch(...args, tryer function, catcher function) -> Promise|any
* ```
*
* @description
* Handles errors with a `tryer` and a `catcher` function. Calls the `tryer` function with the provided arguments and catches any errors thrown by the `tryer` function with the `catcher` function. If the `tryer` function is asynchronous and returns a rejected promise, the `catcher` function will execute with the value of the rejected promise. The `catcher` function is called with the error and all arguments supplied to the `tryer` function.
*
* ```javascript [playground]
* const throwsIfOdd = number => {
* if (number % 2 == 1) {
* throw new Error(`${number} is odd`)
* }
* console.log('did not throw for', number)
* }
*
* const errorHandler = tryCatch(throwsIfOdd, (error, number) => {
* console.log('caught error from number', number)
* console.log(error)
* })
*
* errorHandler(2) // did not throw for 2
* errorHandler(3) // caught error from number 3
* // Error: 3 is odd
*
* ```
*
* `tryCatch` behaves eagerly (executes immediately with a single call and not with multiple calls like a higher order function) when passed any amount of nonfunction (primitive or object) arguments before the `tryer` and `catcher` functions.
*
* ```javascript [playground]
* const add = (a, b) => a + b
*
* tryCatch(1, 2, 3, function throwSum(...numbers) {
* const sum = numbers.reduce(add)
* throw new Error(`the sum is ${sum}`)
* }, function logErrorMessage(error) {
* console.error(error.message) // the sum is 6
* })
* ```
*
* Any promises passed in argument position are resolved for their values before further execution. This only applies to the eager version of the API.
*
* ```javascript [playground]
* tryCatch(Promise.resolve(1), 2, Promise.resolve(3), (a, b, c) => {
* const sum = a + b + c
* if (sum > 5) {
* throw new Error('limit exceeded')
* }
* console.log('sum:', sum)
* }, (error, a, b, c) => {
* console.error(`${a} + ${b} + ${c}: ${error.message}`)
* })
* ```
*/
const tryCatch = function (...args) {
if (args.length > 2) {
const catcher = args.pop(),
tryer = args.pop()
if (areAnyValuesPromises(args)) {
return promiseAll(args)
.then(curry3(_tryCatch, tryer, catcher, __))
}
return _tryCatch(tryer, catcher, args)
}
const tryer = args[0],
catcher = args[1]
return function tryCatcher(...args) {
return _tryCatch(tryer, catcher, args)
}
}
module.exports = tryCatch