rubico
Version:
[a]synchronous functional programming
137 lines (131 loc) • 4.08 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]
* args Array<any>
* argsOrPromises Array<Promise|any>
*
* type SyncOrAsyncFunction = (...args)=>Promise|any
* type SyncOrAsyncCatcher = (error Error|any, ...args)=>Promise|any
*
* tryer SyncOrAsyncFunction
* catcher SyncOrAsyncCatcher
*
* tryCatch(tryer, catcher)(...args) -> Promise|any
* tryCatch(...argsOrPromises, tryer, catcher) -> Promise|any
* ```
*
* @description
* Function equivalent to the [try...catch](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch) statement. Accepts two functions: a `tryer` function and a `catcher` function. Calls the `tryer` function and catches any errors thrown by the `tryer` function with the `catcher` 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 = (error, number) => {
* console.log('caught error from number', number)
* console.log(error)
* }
*
* const handler = tryCatch(throwsIfOdd, errorHandler)
*
* handler(2) // did not throw for 2
* handler(3) // caught error from number 3
* // Error: 3 is odd
*
* ```
*
* If the `tryer` function is asynchronous and throws an error, the `catcher` function will catch the rejected promise.
*
* ```javascript [playground]
* const rejectsIfOdd = async number => {
* if (number % 2 == 1) {
* throw new Error(`${number} is odd`)
* }
* console.log('did not throw for', number)
* }
*
* const errorHandler = (error, number) => {
* console.log('caught error from number', number)
* console.log(error)
* }
*
* const asyncHandler = tryCatch(rejectsIfOdd, errorHandler)
*
* asyncHandler(2) // did not throw for 2
* asyncHandler(3) // caught error from number 3
* // Error: 3 is odd
*
* ```
*
* When provided any number of arguments before the tryer and catcher functions, `tryCatch` executes immediately.
*
* ```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}`)
* })
* ```
*
* See also:
* * [pipe](/docs/pipe)
* * [switchCase](/docs/switchCase)
* * [all](/docs/all)
*/
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