Skip to content

map

對數組中的每個元素執行異步操作,返回包含所有結果的數組。

基礎用法

typescript
import { map } from 'radash'

const numbers = [1, 2, 3, 4, 5]

const results = await map(numbers, async (num) => {
  await new Promise(resolve => setTimeout(resolve, 100))
  return num * 2
})

console.log(results) // [2, 4, 6, 8, 10]

語法

typescript
function map<T, U>(
  array: readonly T[],
  fn: (item: T, index: number, array: readonly T[]) => Promise<U>
): Promise<U[]>

參數

  • array (readonly T[]): 要處理的數組
  • fn (function): 異步映射函數
    • item (T): 當前元素
    • index (number): 當前元素的索引
    • array (readonly T[]): 原數組

返回值

返回一個Promise,解析為映射後的數組。

示例

基本異步映射

typescript
import { map } from 'radash'

const urls = [
  'https://api.example.com/users/1',
  'https://api.example.com/users/2',
  'https://api.example.com/users/3'
]

const users = await map(urls, async (url) => {
  const response = await fetch(url)
  return response.json()
})

console.log(users) // [user1, user2, user3]

處理對象數組

typescript
import { map } from 'radash'

const products = [
  { id: 1, name: 'Laptop', price: 999 },
  { id: 2, name: 'Phone', price: 599 },
  { id: 3, name: 'Tablet', price: 399 }
]

const enrichedProducts = await map(products, async (product) => {
  // 模擬獲取產品詳情
  const details = await fetch(`/api/products/${product.id}/details`)
  const productDetails = await details.json()
  
  return {
    ...product,
    ...productDetails,
    totalPrice: product.price + (productDetails.tax || 0)
  }
})

console.log(enrichedProducts)

處理並發限制

typescript
import { map } from 'radash'

async function processWithConcurrency<T, U>(
  items: T[],
  fn: (item: T) => Promise<U>,
  concurrency: number = 3
): Promise<U[]> {
  const results: U[] = []
  
  for (let i = 0; i < items.length; i += concurrency) {
    const batch = items.slice(i, i + concurrency)
    const batchResults = await map(batch, fn)
    results.push(...batchResults)
  }
  
  return results
}

const urls = Array.from({ length: 10 }, (_, i) => `https://api.example.com/data/${i}`)

const results = await processWithConcurrency(urls, async (url) => {
  const response = await fetch(url)
  return response.json()
}, 3) // 最多3個並發請求

處理錯誤情況

typescript
import { map } from 'radash'

const numbers = [1, 2, 3, 4, 5]

const results = await map(numbers, async (num) => {
  if (num === 3) {
    throw new Error('Number 3 is not allowed')
  }
  
  await new Promise(resolve => setTimeout(resolve, 100))
  return num * 2
})

// 如果任何操作失敗,整個map操作會失敗
console.log(results) // 拋出錯誤

處理超時操作

typescript
import { map } from 'radash'

async function mapWithTimeout<T, U>(
  items: T[],
  fn: (item: T) => Promise<U>,
  timeout: number
): Promise<U[]> {
  return map(items, async (item) => {
    const controller = new AbortController()
    const timeoutId = setTimeout(() => controller.abort(), timeout)
    
    try {
      const result = await Promise.race([
        fn(item),
        new Promise<never>((_, reject) => 
          setTimeout(() => reject(new Error('Timeout')), timeout)
        )
      ])
      
      clearTimeout(timeoutId)
      return result
    } catch (error) {
      clearTimeout(timeoutId)
      throw error
    }
  })
}

const urls = ['https://api.example.com/slow', 'https://api.example.com/fast']

const results = await mapWithTimeout(urls, async (url) => {
  const response = await fetch(url)
  return response.json()
}, 5000) // 5秒超時

處理重試邏輯

typescript
import { map } from 'radash'

async function mapWithRetry<T, U>(
  items: T[],
  fn: (item: T) => Promise<U>,
  maxRetries: number = 3
): Promise<U[]> {
  return map(items, async (item) => {
    for (let attempt = 1; attempt <= maxRetries; attempt++) {
      try {
        return await fn(item)
      } catch (error) {
        if (attempt === maxRetries) {
          throw error
        }
        
        // 指數退避
        await new Promise(resolve => setTimeout(resolve, 1000 * attempt))
      }
    }
  })
}

const urls = ['https://api.example.com/unreliable1', 'https://api.example.com/unreliable2']

const results = await mapWithRetry(urls, async (url) => {
  const response = await fetch(url)
  if (!response.ok) {
    throw new Error(`HTTP ${response.status}`)
  }
  return response.json()
}, 3)

處理進度跟蹤

typescript
import { map } from 'radash'

async function mapWithProgress<T, U>(
  items: T[],
  fn: (item: T, index: number) => Promise<U>,
  onProgress?: (completed: number, total: number) => void
): Promise<U[]> {
  const results: U[] = []
  
  for (let i = 0; i < items.length; i++) {
    const result = await fn(items[i], i)
    results.push(result)
    
    if (onProgress) {
      onProgress(i + 1, items.length)
    }
  }
  
  return results
}

const tasks = ['task1', 'task2', 'task3', 'task4', 'task5']

const results = await mapWithProgress(tasks, async (task, index) => {
  await new Promise(resolve => setTimeout(resolve, 1000))
  return `Completed ${task}`
}, (completed, total) => {
  console.log(`Progress: ${completed}/${total} (${Math.round(completed/total*100)}%)`)
})

處理條件映射

typescript
import { map } from 'radash'

const users = [
  { id: 1, name: 'Alice', role: 'admin' },
  { id: 2, name: 'Bob', role: 'user' },
  { id: 3, name: 'Charlie', role: 'admin' }
]

const results = await map(users, async (user) => {
  if (user.role === 'admin') {
    // 管理員需要額外的權限檢查
    const permissions = await fetch(`/api/users/${user.id}/permissions`)
    const userPermissions = await permissions.json()
    
    return {
      ...user,
      permissions: userPermissions
    }
  } else {
    // 普通用戶只需要基本信息
    return {
      ...user,
      permissions: ['read']
    }
  }
})

console.log(results)

處理嵌套數據

typescript
import { map } from 'radash'

interface Post {
  id: number
  title: string
  authorId: number
}

interface Author {
  id: number
  name: string
  email: string
}

async function enrichPosts(posts: Post[]): Promise<(Post & { author: Author })[]> {
  return map(posts, async (post) => {
    const authorResponse = await fetch(`/api/authors/${post.authorId}`)
    const author = await authorResponse.json()
    
    return {
      ...post,
      author
    }
  })
}

const posts: Post[] = [
  { id: 1, title: 'First Post', authorId: 1 },
  { id: 2, title: 'Second Post', authorId: 2 },
  { id: 3, title: 'Third Post', authorId: 1 }
]

const enrichedPosts = await enrichPosts(posts)
console.log(enrichedPosts)

處理批量操作

typescript
import { map } from 'radash'

async function processBatch<T, U>(
  items: T[],
  batchSize: number,
  fn: (batch: T[]) => Promise<U[]>
): Promise<U[]> {
  const batches: T[][] = []
  
  for (let i = 0; i < items.length; i += batchSize) {
    batches.push(items.slice(i, i + batchSize))
  }
  
  const batchResults = await map(batches, fn)
  
  return batchResults.flat()
}

const userIds = Array.from({ length: 100 }, (_, i) => i + 1)

const users = await processBatch(userIds, 10, async (batch) => {
  const response = await fetch('/api/users/batch', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ ids: batch })
  })
  
  return response.json()
})

console.log(`Processed ${users.length} users`)

處理文件操作

typescript
import { map } from 'radash'

async function processFiles(filePaths: string[]) {
  return map(filePaths, async (filePath) => {
    const fs = await import('fs/promises')
    const content = await fs.readFile(filePath, 'utf-8')
    
    // 處理文件內容
    const processed = content.toUpperCase()
    
    // 寫入處理後的文件
    const outputPath = filePath.replace('.txt', '.processed.txt')
    await fs.writeFile(outputPath, processed)
    
    return {
      inputPath: filePath,
      outputPath,
      size: processed.length
    }
  })
}

const files = ['./file1.txt', './file2.txt', './file3.txt']

const results = await processFiles(files)
console.log(results)

注意事項

  1. 並發執行: 所有異步操作會並發執行
  2. 錯誤處理: 如果任何操作失敗,整個map操作會失敗
  3. 性能: 適合I/O密集型操作,不適合CPU密集型操作
  4. 內存: 所有結果會同時保存在內存中
  5. 順序: 結果數組的順序與輸入數組的順序相同

與其他方法的區別

  • Array.prototype.map(): 同步操作,不支持異步
  • Promise.all(): 需要手動創建Promise數組
  • map(): radash提供的簡潔的異步映射方法

實際應用場景

  1. API調用: 批量獲取數據
  2. 文件處理: 批量處理文件
  3. 數據庫操作: 批量查詢或更新
  4. 圖像處理: 批量處理圖像
  5. 數據轉換: 批量轉換數據格式

Released under the MIT License.