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.