Skip to content

tryit

Safely execute async functions, returning [error, result] tuples.

Syntax

typescript
tryit<T>(
  fn: () => Promise<T>
): Promise<[Error | null, T | null]>

tryit<T, Args extends any[]>(
  fn: (...args: Args) => Promise<T>,
  ...args: Args
): Promise<[Error | null, T | null]>

Parameters

  • fn (() => Promise<T> | (...args: Args) => Promise<T>): The async function to execute
  • ...args (Args, optional): Arguments to pass to the function

Return Value

  • Promise<[Error | null, T | null]>: Tuple containing error and result

Examples

Basic Usage

typescript
import { tryit } from 'radash'

const fetchUser = async (id: number) => {
  const response = await fetch(`/api/users/${id}`)
  return response.json()
}

const [err, user] = await tryit(fetchUser, 123)

if (err) {
  console.error('Failed to fetch user:', err)
} else {
  console.log('User:', user)
}

Handle Network Requests

typescript
import { tryit } from 'radash'

const [err, data] = await tryit(fetch)('/api/users')

if (err) {
  console.error('Network error:', err)
  return
}

const users = await data.json()
console.log('Users:', users)

Handle File Operations

typescript
import { tryit } from 'radash'
import { readFile } from 'fs/promises'

const [err, content] = await tryit(readFile)('config.json', 'utf8')

if (err) {
  console.error('Failed to read file:', err)
  // Use default config
  return defaultConfig
}

const config = JSON.parse(content)
console.log('Config loaded:', config)

Handle Database Operations

typescript
import { tryit } from 'radash'

const createUser = async (userData: any) => {
  return await db.query(
    'INSERT INTO users (name, email) VALUES (?, ?)',
    [userData.name, userData.email]
  )
}

const [err, result] = await tryit(createUser, {
  name: 'John Doe',
  email: 'john@example.com'
})

if (err) {
  console.error('Failed to create user:', err)
  return
}

console.log('User created:', result)

Handle Multiple Operations

typescript
import { tryit } from 'radash'

async function processUserData(userId: number) {
  // Fetch user data
  const [userErr, user] = await tryit(async () => {
    const response = await fetch(`/api/users/${userId}`)
    return response.json()
  })

  if (userErr) {
    console.error('Failed to fetch user:', userErr)
    return null
  }

  // Fetch user posts
  const [postsErr, posts] = await tryit(async () => {
    const response = await fetch(`/api/users/${userId}/posts`)
    return response.json()
  })

  if (postsErr) {
    console.error('Failed to fetch posts:', postsErr)
    return { user, posts: [] }
  }

  return { user, posts }
}

const result = await processUserData(123)
console.log('User data:', result)

Handle Validation

typescript
import { tryit } from 'radash'

const validateUser = async (userData: any) => {
  if (!userData.email || !userData.email.includes('@')) {
    throw new Error('Invalid email')
  }
  
  if (!userData.age || userData.age < 0) {
    throw new Error('Invalid age')
  }
  
  return userData
}

const [err, validatedUser] = await tryit(validateUser, {
  name: 'Alice',
  email: 'alice@example.com',
  age: 25
})

if (err) {
  console.error('Validation failed:', err.message)
  return
}

console.log('User validated:', validatedUser)

Handle Timeout Operations

typescript
import { tryit } from 'radash'

const fetchWithTimeout = async (url: string, timeout: number) => {
  const controller = new AbortController()
  const timeoutId = setTimeout(() => controller.abort(), timeout)
  
  try {
    const response = await fetch(url, { signal: controller.signal })
    clearTimeout(timeoutId)
    
    if (!response.ok) {
      throw new Error(`HTTP ${response.status}`)
    }
    
    return response.json()
  } catch (error) {
    clearTimeout(timeoutId)
    throw error
  }
}

const [err, data] = await tryit(fetchWithTimeout)('https://api.example.com/data', 5000)

if (err) {
  console.error('Request failed:', err.message)
  return
}

console.log('Data received:', data)

Handle Batch Operations

typescript
import { tryit } from 'radash'

async function processBatch(items: any[]) {
  const results = []
  
  for (const item of items) {
    const [err, result] = await tryit(async () => {
      // Simulate processing
      await new Promise(resolve => setTimeout(resolve, 100))
      
      if (Math.random() < 0.1) {
        throw new Error('Random processing error')
      }
      
      return { ...item, processed: true }
    })
    
    if (err) {
      console.error(`Failed to process item:`, err.message)
      results.push({ ...item, error: err.message })
    } else {
      results.push(result)
    }
  }
  
  return results
}

const items = [{ id: 1 }, { id: 2 }, { id: 3 }]
const processed = await processBatch(items)
console.log('Processed items:', processed)

Handle Conditional Operations

typescript
import { tryit } from 'radash'

const processWithCondition = async (data: any, shouldProcess: boolean) => {
  if (!shouldProcess) {
    throw new Error('Processing not allowed')
  }
  
  // Simulate processing
  await new Promise(resolve => setTimeout(resolve, 1000))
  
  return { ...data, processed: true }
}

const [err, result] = await tryit(processWithCondition)({ id: 1, name: 'test' }, true)

if (err) {
  console.error('Processing failed:', err.message)
  return
}

console.log('Processing successful:', result)

Handle Complex Error Handling

typescript
import { tryit } from 'radash'

class CustomError extends Error {
  constructor(message: string, public code: string) {
    super(message)
    this.name = 'CustomError'
  }
}

const complexOperation = async (input: any) => {
  if (!input.id) {
    throw new CustomError('Missing ID', 'VALIDATION_ERROR')
  }
  
  if (input.id < 0) {
    throw new CustomError('Invalid ID', 'INVALID_INPUT')
  }
  
  // Simulate async work
  await new Promise(resolve => setTimeout(resolve, 500))
  
  return { ...input, processed: true }
}

const [err, result] = await tryit(complexOperation)({ id: 123, name: 'test' })

if (err) {
  if (err instanceof CustomError) {
    switch (err.code) {
      case 'VALIDATION_ERROR':
        console.error('Validation error:', err.message)
        break
      case 'INVALID_INPUT':
        console.error('Invalid input:', err.message)
        break
      default:
        console.error('Unknown error:', err.message)
    }
  } else {
    console.error('Unexpected error:', err.message)
  }
  return
}

console.log('Operation successful:', result)

Handle API Rate Limiting

typescript
import { tryit } from 'radash'

const fetchWithRateLimit = async (url: string) => {
  const response = await fetch(url)
  
  if (response.status === 429) {
    throw new Error('Rate limited')
  }
  
  if (!response.ok) {
    throw new Error(`HTTP ${response.status}`)
  }
  
  return response.json()
}

const [err, data] = await tryit(fetchWithRateLimit)('https://api.example.com/data')

if (err) {
  if (err.message === 'Rate limited') {
    console.log('Rate limited, will retry later')
    // Implement retry logic
  } else {
    console.error('Request failed:', err.message)
  }
  return
}

console.log('Data received:', data)

Handle File System Operations

typescript
import { tryit } from 'radash'
import { readFile, writeFile, unlink } from 'fs/promises'

async function processFile(inputPath: string, outputPath: string) {
  // Read file
  const [readErr, content] = await tryit(readFile)(inputPath, 'utf8')
  
  if (readErr) {
    console.error('Failed to read file:', readErr.message)
    return
  }
  
  // Process content
  const processed = content.toUpperCase()
  
  // Write processed file
  const [writeErr] = await tryit(writeFile)(outputPath, processed)
  
  if (writeErr) {
    console.error('Failed to write file:', writeErr.message)
    return
  }
  
  // Clean up original file
  const [deleteErr] = await tryit(unlink)(inputPath)
  
  if (deleteErr) {
    console.error('Failed to delete original file:', deleteErr.message)
  }
  
  return { inputPath, outputPath, size: processed.length }
}

const result = await processFile('./input.txt', './output.txt')
console.log('File processed:', result)

Handle Database Transactions

typescript
import { tryit } from 'radash'

const executeTransaction = async (operations: any[]) => {
  // Start transaction
  await db.beginTransaction()
  
  try {
    const results = []
    
    for (const operation of operations) {
      const [err, result] = await tryit(async () => {
        return await db.query(operation.query, operation.params)
      })
      
      if (err) {
        throw err
      }
      
      results.push(result)
    }
    
    // Commit transaction
    await db.commit()
    return results
    
  } catch (error) {
    // Rollback transaction
    await db.rollback()
    throw error
  }
}

const operations = [
  { query: 'INSERT INTO users (name) VALUES (?)', params: ['Alice'] },
  { query: 'INSERT INTO users (name) VALUES (?)', params: ['Bob'] }
]

const [err, results] = await tryit(executeTransaction)(operations)

if (err) {
  console.error('Transaction failed:', err.message)
  return
}

console.log('Transaction successful:', results)

Notes

  1. Error handling: Returns errors instead of throwing them
  2. Type safety: Provides full TypeScript support
  3. Performance: Minimal overhead compared to try-catch
  4. Consistent format: Always returns [error, result] tuple
  5. Async support: Designed specifically for async functions

Differences from Other Functions

  • tryit: Returns error and result tuple
  • guard: Returns object with error and data properties
  • try-catch: Throws errors instead of returning them
  • tryit(): More functional approach to error handling

Performance

  • Time Complexity: O(1) overhead
  • Memory: Minimal additional memory usage
  • Use Cases: API calls, file operations, database queries

Released under the MIT License.