Skip to content

guard

保護異步函數,捕獲錯誤並返回統一的結果格式。

基礎用法

typescript
import { guard } from 'radash'

const result = await guard(async () => {
  const response = await fetch('https://api.example.com/data')
  return response.json()
})

if (result.error) {
  console.log('Error:', result.error)
} else {
  console.log('Data:', result.data)
}

語法

typescript
function guard<T>(
  fn: () => Promise<T>
): Promise<{ data: T | null; error: Error | null }>

參數

  • fn (function): 要保護的異步函數

返回值

返回一個Promise,解析為包含data和error的對象。

示例

基本錯誤處理

typescript
import { guard } from 'radash'

async function fetchUserData(userId: number) {
  const result = await guard(async () => {
    const response = await fetch(`/api/users/${userId}`)
    if (!response.ok) {
      throw new Error(`HTTP ${response.status}: ${response.statusText}`)
    }
    return response.json()
  })

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

  return result.data
}

const user = await fetchUserData(123)

處理網絡請求

typescript
import { guard } from 'radash'

async function makeApiCall(url: string) {
  const result = await guard(async () => {
    const response = await fetch(url)
    if (!response.ok) {
      throw new Error(`Request failed: ${response.status}`)
    }
    return response.json()
  })

  return result
}

const apiResult = await makeApiCall('https://api.example.com/data')
if (apiResult.error) {
  console.log('API Error:', apiResult.error.message)
} else {
  console.log('API Data:', apiResult.data)
}

處理數據庫操作

typescript
import { guard } from 'radash'

async function createUser(userData: any) {
  const result = await guard(async () => {
    // 模擬數據庫操作
    if (userData.email.includes('test')) {
      throw new Error('Test emails are not allowed')
    }
    
    return {
      id: Date.now(),
      ...userData,
      createdAt: new Date()
    }
  })

  if (result.error) {
    console.error('User creation failed:', result.error.message)
    return null
  }

  return result.data
}

const newUser = await createUser({
  name: 'John Doe',
  email: 'john@example.com'
})

處理文件操作

typescript
import { guard } from 'radash'

async function readFileContent(filePath: string) {
  const result = await guard(async () => {
    const fs = await import('fs/promises')
    const content = await fs.readFile(filePath, 'utf-8')
    return JSON.parse(content)
  })

  if (result.error) {
    console.error('File read error:', result.error.message)
    return null
  }

  return result.data
}

const config = await readFileContent('./config.json')
if (config) {
  console.log('Config loaded:', config)
}

處理多個異步操作

typescript
import { guard } from 'radash'

async function processMultipleTasks() {
  const tasks = [
    guard(async () => {
      await new Promise(resolve => setTimeout(resolve, 100))
      return 'Task 1 completed'
    }),
    guard(async () => {
      await new Promise(resolve => setTimeout(resolve, 200))
      throw new Error('Task 2 failed')
    }),
    guard(async () => {
      await new Promise(resolve => setTimeout(resolve, 150))
      return 'Task 3 completed'
    })
  ]

  const results = await Promise.all(tasks)
  
  results.forEach((result, index) => {
    if (result.error) {
      console.log(`Task ${index + 1} failed:`, result.error.message)
    } else {
      console.log(`Task ${index + 1} succeeded:`, result.data)
    }
  })
}

await processMultipleTasks()

處理超時操作

typescript
import { guard } from 'radash'

async function fetchWithTimeout(url: string, timeout: number) {
  const result = await guard(async () => {
    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
    }
  })

  return result
}

const data = await fetchWithTimeout('https://api.example.com/data', 5000)
if (data.error) {
  console.log('Request failed:', data.error.message)
} else {
  console.log('Data received:', data.data)
}

處理重試邏輯

typescript
import { guard } from 'radash'

async function fetchWithRetry(url: string, maxRetries: number = 3) {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    const result = await guard(async () => {
      const response = await fetch(url)
      if (!response.ok) {
        throw new Error(`HTTP ${response.status}`)
      }
      return response.json()
    })

    if (!result.error) {
      return result.data
    }

    console.log(`Attempt ${attempt} failed:`, result.error.message)
    
    if (attempt < maxRetries) {
      await new Promise(resolve => setTimeout(resolve, 1000 * attempt))
    }
  }

  throw new Error(`Failed after ${maxRetries} attempts`)
}

try {
  const data = await fetchWithRetry('https://api.example.com/data')
  console.log('Success:', data)
} catch (error) {
  console.error('All attempts failed:', error.message)
}

處理並發操作

typescript
import { guard } from 'radash'

async function processConcurrentRequests(urls: string[]) {
  const requests = urls.map(url =>
    guard(async () => {
      const response = await fetch(url)
      if (!response.ok) {
        throw new Error(`HTTP ${response.status}`)
      }
      return response.json()
    })
  )

  const results = await Promise.all(requests)
  
  const successful = results.filter(r => !r.error)
  const failed = results.filter(r => r.error)
  
  console.log(`Successful: ${successful.length}, Failed: ${failed.length}`)
  
  return {
    successful: successful.map(r => r.data),
    failed: failed.map(r => r.error)
  }
}

const urls = [
  'https://api.example.com/users',
  'https://api.example.com/posts',
  'https://api.example.com/comments'
]

const { successful, failed } = await processConcurrentRequests(urls)

處理條件操作

typescript
import { guard } from 'radash'

async function conditionalOperation(shouldFail: boolean) {
  const result = await guard(async () => {
    if (shouldFail) {
      throw new Error('Operation failed as requested')
    }
    
    // 模擬成功操作
    await new Promise(resolve => setTimeout(resolve, 100))
    return { success: true, timestamp: Date.now() }
  })

  return result
}

// 成功的情況
const successResult = await conditionalOperation(false)
if (successResult.data) {
  console.log('Operation succeeded:', successResult.data)
}

// 失敗的情況
const failureResult = await conditionalOperation(true)
if (failureResult.error) {
  console.log('Operation failed:', failureResult.error.message)
}

處理復雜錯誤處理

typescript
import { guard } from 'radash'

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

async function complexOperation() {
  const result = await guard(async () => {
    // 模擬復雜的業務邏輯
    const random = Math.random()
    
    if (random < 0.3) {
      throw new CustomError('Network error', 'NETWORK_ERROR')
    } else if (random < 0.6) {
      throw new CustomError('Validation error', 'VALIDATION_ERROR')
    } else if (random < 0.8) {
      throw new CustomError('Server error', 'SERVER_ERROR')
    }
    
    return { message: 'Operation completed successfully' }
  })

  if (result.error) {
    if (result.error instanceof CustomError) {
      switch (result.error.code) {
        case 'NETWORK_ERROR':
          console.log('Network issue, retrying...')
          break
        case 'VALIDATION_ERROR':
          console.log('Invalid input data')
          break
        case 'SERVER_ERROR':
          console.log('Server is down')
          break
      }
    } else {
      console.log('Unknown error:', result.error.message)
    }
    return null
  }

  return result.data
}

const result = await complexOperation()

注意事項

  1. 錯誤捕獲: 捕獲所有類型的錯誤,包括同步和異步錯誤
  2. 統一格式: 返回統一的結果格式,便於處理
  3. 類型安全: 提供完整的TypeScript類型支持
  4. 性能: 對性能影響很小,只是簡單的錯誤包裝
  5. 鏈式調用: 可以與其他異步函數鏈式調用

與其他方法的區別

  • try/catch: 需要手動處理錯誤,guard 提供統一格式
  • Promise.catch(): 只處理Promise錯誤,guard 處理所有錯誤
  • guard(): radash提供的簡潔的錯誤處理工具

實際應用場景

  1. API調用: 處理網絡請求錯誤
  2. 數據庫操作: 處理數據庫連接和查詢錯誤
  3. 文件操作: 處理文件讀寫錯誤
  4. 第三方服務: 處理外部服務調用錯誤
  5. 業務邏輯: 保護復雜的業務操作

Released under the MIT License.