JavaScript的异步编程是现代Web开发的核心概念之一。从最初的回调函数到现在的Async/Await,JavaScript异步编程经历了巨大的演进。本文将深入探讨异步编程的最佳实践,帮助你写出更清晰、更可维护的异步代码。

异步编程演进史

1. 回调函数时代

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 传统的回调函数
function fetchData(callback) {
  setTimeout(() => {
    callback('Data loaded')
  }, 1000)
}

function processData(callback) {
  fetchData((data) => {
    const result = data.toUpperCase()
    callback(result)
  })
}

// 回调地狱
function getData(callback) {
  fetchData((data1) => {
    processData((data2) => {
      anotherProcess((data3) => {
        finalProcess((data4) => {
          callback(data4)
        })
      })
    })
  })
}

2. Promise时代

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
// Promise基础用法
function fetchData() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('Data loaded')
    }, 1000)
  })
}

// Promise链
fetchData()
  .then(data => {
    console.log(data)
    return processData(data)
  })
  .then(result => {
    console.log(result)
    return anotherProcess(result)
  })
  .catch(error => {
    console.error('Error:', error)
  })

3. Async/Await时代

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// Async/Await基础用法
async function getData() {
  try {
    const data = await fetchData()
    const result = await processData(data)
    const finalResult = await anotherProcess(result)
    return finalResult
  } catch (error) {
    console.error('Error:', error)
    throw error
  }
}

Promise深度解析

1. Promise的三种状态

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// Pending(进行中)
const promise = new Promise((resolve, reject) => {
  // 异步操作进行中
  setTimeout(() => {
    resolve('Success') // Fulfilled(已完成)
  }, 1000)
})

console.log(promise) // Promise { <pending> }

// 状态变化图解
// Pending → Fulfilled
// Pending → Rejected
// 状态一旦确定就不可逆转

2. Promise构造函数的正确使用

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
// ❌ 错误用法:在构造函数中同步调用resolve
function badPromise() {
  const promise = new Promise((resolve) => {
    resolve('Already resolved')
    // 这里的代码永远不会执行
    console.log('This will never run')
  })
  return promise
}

// ✅ 正确用法:异步调用resolve
function goodPromise() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('Resolved asynchronously')
      console.log('This will run')
    }, 1000)
  })
}

3. Promise的错误处理

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// Promise错误处理的几种方式
// 1. 使用catch()
fetch('/api/data')
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => console.error(error))

// 2. 使用第二个参数
fetch('/api/data')
  .then(
    response => response.json(),
    error => console.error(error)
  )

// 3. 使用try-catch包装
function async fetchWithErrorHandling() {
  try {
    const response = await fetch('/api/data')
    const data = await response.json()
    console.log(data)
  } catch (error) {
    console.error(error)
  }
}

4. Promise组合方法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
// Promise.all - 所有Promise都完成
function fetchMultipleData() {
  const promise1 = fetch('/api/users')
  const promise2 = fetch('/api/posts')
  const promise3 = fetch('/api/comments')

  return Promise.all([promise1, promise2, promise3])
    .then(([users, posts, comments]) => {
      console.log('All data loaded')
      return { users, posts, comments }
    })
    .catch(error => {
      console.error('One of the requests failed:', error)
      throw error
    })
}

// Promise.allSettled - 所有Promise都完成(不论成功失败)
function fetchAllSettled() {
  const urls = ['/api/users', '/api/posts', '/api/comments']

  const promises = urls.map(url =>
    fetch(url).then(response => response.json())
  )

  return Promise.allSettled(promises)
    .then(results => {
      const successful = results.filter(result => result.status === 'fulfilled')
      const failed = results.filter(result => result.status === 'rejected')

      console.log('Successful requests:', successful.length)
      console.log('Failed requests:', failed.length)

      return {
        successful: successful.map(r => r.value),
        failed: failed.map(f => f.reason)
      }
    })
}

// Promise.race - 最快完成的Promise
function fetchWithRace() {
  const timeoutPromise = new Promise((_, reject) => {
    setTimeout(() => reject(new Error('Request timeout')), 5000)
  })

  const fetchPromise = fetch('/api/data')
    .then(response => response.json())

  return Promise.race([fetchPromise, timeoutPromise])
    .catch(error => {
      if (error.message === 'Request timeout') {
        console.log('请求超时')
      }
      throw error
    })
}

Async/Await最佳实践

1. 错误处理策略

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
// 1. 统一的错误处理中间件
class ApiClient {
  async request(url, options = {}) {
    try {
      const response = await fetch(url, {
        headers: {
          'Content-Type': 'application/json',
          ...options.headers,
        },
        ...options,
      })

      if (!response.ok) {
        throw new Error(`HTTP Error: ${response.status} ${response.statusText}`)
      }

      return await response.json()
    } catch (error) {
      if (error instanceof TypeError) {
        throw new Error('Network error: ' + error.message)
      }
      throw error
    }
  }
}

// 2. 自定义错误类
class NetworkError extends Error {
  constructor(message, response) {
    super(message)
    this.name = 'NetworkError'
    this.response = response
  }
}

class ValidationError extends Error {
  constructor(message, field) {
    super(message)
    this.name = 'ValidationError'
    this.field = field
  }
}

// 使用示例
const apiClient = new ApiClient()

async function getUserData(userId) {
  try {
    const userData = await apiClient.request(`/api/users/${userId}`)

    if (!userData.email) {
      throw new ValidationError('Email is required', 'email')
    }

    return userData
  } catch (error) {
    if (error instanceof ValidationError) {
      console.error(`Validation error in ${error.field}: ${error.message}`)
    } else if (error instanceof NetworkError) {
      console.error('Network error:', error.message)
    } else {
      console.error('Unexpected error:', error.message)
    }
    throw error
  }
}

2. 并发控制

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
// 并发限制器
class ConcurrencyLimiter {
  constructor(maxConcurrent) {
    this.maxConcurrent = maxConcurrent
    this.currentCount = 0
    this.queue = []
  }

  async execute(task) {
    return new Promise((resolve, reject) => {
      this.queue.push({ task, resolve, reject })
      this.processQueue()
    })
  }

  async processQueue() {
    if (this.currentCount >= this.maxConcurrent || this.queue.length === 0) {
      return
    }

    this.currentCount++
    const { task, resolve, reject } = this.queue.shift()

    try {
      const result = await task()
      resolve(result)
    } catch (error) {
      reject(error)
    } finally {
      this.currentCount--
      this.processQueue()
    }
  }
}

// 使用示例
const limiter = new ConcurrencyLimiter(3)

async function processBatch(items) {
  const tasks = items.map(item =>
    limiter.execute(() => processItem(item))
  )

  return Promise.all(tasks)
}

async function processItem(item) {
  console.log(`Processing item: ${item}`)
  await new Promise(resolve => setTimeout(resolve, 1000))
  return `Processed: ${item}`
}

3. 重试机制

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
// 指数退避重试
async function retryWithBackoff(fn, maxRetries = 3, baseDelay = 1000) {
  let lastError

  for (let attempt = 0; attempt <= maxRetries; attempt++) {
    try {
      const result = await fn()
      return result
    } catch (error) {
      lastError = error

      if (attempt === maxRetries) {
        throw error
      }

      const delay = baseDelay * Math.pow(2, attempt)
      console.log(`Attempt ${attempt + 1} failed, retrying in ${delay}ms`)

      await new Promise(resolve => setTimeout(resolve, delay))
    }
  }

  throw lastError
}

// 线性退避重试
async function retryLinear(fn, maxRetries = 3, delay = 1000) {
  let lastError

  for (let attempt = 0; attempt <= maxRetries; attempt++) {
    try {
      const result = await fn()
      return result
    } catch (error) {
      lastError = error

      if (attempt === maxRetries) {
        throw error
      }

      const waitTime = delay * (attempt + 1)
      console.log(`Attempt ${attempt + 1} failed, retrying in ${waitTime}ms`)

      await new Promise(resolve => setTimeout(resolve, waitTime))
    }
  }

  throw lastError
}

// 使用示例
async function fetchWithRetry(url) {
  return retryWithBackoff(async () => {
    const response = await fetch(url)
    if (!response.ok) {
      throw new Error(`HTTP ${response.status}`)
    }
    return response.json()
  }, 5)
}

4. 超时控制

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
// 带超时的fetch
async function fetchWithTimeout(url, timeout = 5000) {
  const controller = new AbortController()
  const timeoutId = setTimeout(() => controller.abort(), timeout)

  try {
    const response = await fetch(url, {
      signal: controller.signal,
    })

    if (!response.ok) {
      throw new Error(`HTTP Error: ${response.status}`)
    }

    return response.json()
  } finally {
    clearTimeout(timeoutId)
  }
}

// Promise.race实现的超时
async function fetchWithTimeoutRace(url, timeout = 5000) {
  const fetchPromise = fetch(url).then(response => {
    if (!response.ok) {
      throw new Error(`HTTP Error: ${response.status}`)
    })
    return response.json()
  })

  const timeoutPromise = new Promise((_, reject) => {
    setTimeout(() => reject(new Error('Request timeout')), timeout)
  })

  return Promise.race([fetchPromise, timeoutPromise])
}

// 使用示例
try {
  const data = await fetchWithTimeout('/api/data', 3000)
  console.log(data)
} catch (error) {
  if (error.name === 'AbortError') {
    console.log('Request was aborted due to timeout')
  } else {
    console.error('Error:', error.message)
  }
}

高级异步模式

1. 并行处理

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// 并行处理数组中的异步操作
async function processItemsInParallel(items, processor) {
  const promises = items.map(item => processor(item))
  const results = await Promise.all(promises)
  return results
}

// 分批并行处理
async function processItemsInBatches(items, processor, batchSize = 5) {
  const results = []

  for (let i = 0; i < items.length; i += batchSize) {
    const batch = items.slice(i, i + batchSize)
    const batchResults = await Promise.all(
      batch.map(item => processor(item))
    )
    results.push(...batchResults)
  }

  return results
}

// 使用示例
async function fetchUsersInParallel(userIds) {
  const fetchUser = async (id) => {
    const response = await fetch(`/api/users/${id}`)
    return response.json()
  }

  const users = await processItemsInParallel(userIds, fetchUser)
  return users
}

2. 瀡道模式

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
// 异步管道
class AsyncPipeline {
  constructor() {
    this.steps = []
  }

  step(fn) {
    this.steps.push(fn)
    return this
  }

  async execute(data) {
    let result = data

    for (const step of this.steps) {
      result = await step(result)
    }

    return result
  }
}

// 使用示例
const pipeline = new AsyncPipeline()

pipeline
  .step(async (data) => {
    console.log('Step 1: Data validation')
    if (!data.id) {
      throw new Error('ID is required')
    }
    return { ...data, validated: true }
  })
  .step(async (data) => {
    console.log('Step 2: Data transformation')
    return { ...data, transformed: true }
  })
  .step(async (data) => {
    console.log('Step 3: Data saving')
    await saveData(data)
    return { ...data, saved: true }
  })

const result = await pipeline.execute({ id: 1, name: 'Test' })

3. 发布订阅模式

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
// 异步事件发射器
class AsyncEventEmitter {
  constructor() {
    this.listeners = new Map()
  }

  on(event, listener) {
    if (!this.listeners.has(event)) {
      this.listeners.set(event, [])
    }
    this.listeners.get(event).push(listener)
    return this
  }

  off(event, listener) {
    if (this.listeners.has(event)) {
      const listeners = this.listeners.get(event)
      const index = listeners.indexOf(listener)
      if (index !== -1) {
        listeners.splice(index, 1)
      }
    }
    return this
  }

  async emit(event, ...args) {
    if (!this.listeners.has(event)) {
      return
    }

    const listeners = this.listeners.get(event)
    const promises = listeners.map(listener =>
      Promise.resolve(listener(...args))
    )

    await Promise.all(promises)
  }

  async emitSeries(event, ...args) {
    if (!this.listeners.has(event)) {
      return
    }

    const listeners = this.listeners.get(event)
    for (const listener of listeners) {
      await Promise.resolve(listener(...args))
    }
  }
}

// 使用示例
const emitter = new AsyncEventEmitter()

emitter.on('data', async (data) => {
  console.log('Listener 1 received data:', data)
  await processData(data)
})

emitter.on('data', async (data) => {
  console.log('Listener 2 received data:', data)
  await saveData(data)
})

await emitter.emit('data', { id: 1, name: 'Test' })

异步模式在React中的应用

1. 数据获取Hook

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
import { useState, useEffect, useCallback } from 'react'

// 自定义数据获取Hook
function useApi(url, options = {}) {
  const [data, setData] = useState(null)
  const [loading, setLoading] = useState(true)
  const [error, setError] = useState(null)

  const fetchData = useCallback(async () => {
    try {
      setLoading(true)
      setError(null)

      const response = await fetch(url, options)
      if (!response.ok) {
        throw new Error(`HTTP error! status: ${response.status}`)
      }

      const result = await response.json()
      setData(result)
    } catch (err) {
      setError(err.message)
    } finally {
      setLoading(false)
    }
  }, [url, options])

  useEffect(() => {
    fetchData()
  }, [fetchData])

  return { data, loading, error, refetch: fetchData }
}

// 使用示例
function UserProfile({ userId }) {
  const { data: user, loading, error, refetch } = useApi(`/api/users/${userId}`)

  if (loading) return <div>Loading...</div>
  if (error) return <div>Error: {error}</div>
  if (!user) return <div>No user found</div>

  return (
    <div>
      <h1>{user.name}</h1>
      <p>{user.email}</p>
      <button onClick={refetch}>Refresh</button>
    </div>
  )
}

2. 表单提交处理

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
import { useState } from 'react'

function useAsyncForm(initialValues, submitHandler) {
  const [values, setValues] = useState(initialValues)
  const [errors, setErrors] = useState({})
  const [isSubmitting, setIsSubmitting] = useState(false)

  const handleChange = useCallback((e) => {
    const { name, value, type, checked } = e.target
    const fieldValue = type === 'checkbox' ? checked : value

    setValues(prev => ({
      ...prev,
      [name]: fieldValue
    }))

    if (errors[name]) {
      setErrors(prev => ({
        ...prev,
        [name]: ''
      }))
    }
  }, [errors])

  const handleSubmit = useCallback(async (e) => {
    e.preventDefault()
    setIsSubmitting(true)
    setErrors({})

    try {
      await submitHandler(values)
    } catch (error) {
      if (error.validationErrors) {
        setErrors(error.validationErrors)
      } else {
        console.error('Submit error:', error)
      }
    } finally {
      setIsSubmitting(false)
    }
  }, [values, submitHandler])

  const reset = useCallback(() => {
    setValues(initialValues)
    setErrors({})
    setIsSubmitting(false)
  }, [initialValues])

  return {
    values,
    errors,
    isSubmitting,
    handleChange,
    handleSubmit,
    reset
  }
}

// 使用示例
function UserForm() {
  const submitHandler = async (values) => {
    const response = await fetch('/api/users', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(values),
    })

    if (!response.ok) {
      const error = await response.json()
      throw new Error(error.message || 'Submit failed')
    }

    return response.json()
  }

  const {
    values,
    errors,
    isSubmitting,
    handleChange,
    handleSubmit,
    reset
  } = useAsyncForm({ name: '', email: '' }, submitHandler)

  return (
    <form onSubmit={handleSubmit}>
      <div>
        <label>Name:</label>
        <input
          type="text"
          name="name"
          value={values.name}
          onChange={handleChange}
        />
        {errors.name && <span style={{ color: 'red' }}>{errors.name}</span>}
      </div>

      <div>
        <label>Email:</label>
        <input
          type="email"
          name="email"
          value={values.email}
          onChange={handleChange}
        />
        {errors.email && <span style={{ color: 'red' }}>{errors.email}</span>}
      </div>

      <button type="submit" disabled={isSubmitting}>
        {isSubmitting ? 'Submitting...' : 'Submit'}
      </button>
      <button type="button" onClick={reset}>
        Reset
      </button>
    </form>
  )
}

性能优化技巧

1. 请求缓存

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
// 简单的内存缓存
class Cache {
  constructor(ttl = 5 * 60 * 1000) { // 5分钟TTL
    this.cache = new Map()
    this.ttl = ttl
  }

  set(key, value) {
    this.cache.set(key, {
      value,
      timestamp: Date.now()
    })
  }

  get(key) {
    const item = this.cache.get(key)
    if (!item) {
      return null
    }

    if (Date.now() - item.timestamp > this.ttl) {
      this.cache.delete(key)
      return null
    }

    return item.value
  }

  clear() {
    this.cache.clear()
  }
}

// 带缓存的API客户端
class CachedApiClient {
  constructor() {
    this.cache = new Cache()
  }

  async get(url, options = {}) {
    const cacheKey = `${url}:${JSON.stringify(options)}`
    const cachedData = this.cache.get(cacheKey)

    if (cachedData) {
      console.log('Returning cached data for:', url)
      return cachedData
    }

    console.log('Fetching fresh data for:', url)
    const response = await fetch(url, options)
    const data = await response.json()

    this.cache.set(cacheKey, data)
    return data
  }
}

// 使用示例
const apiClient = new CachedApiClient()

// 第一次请求 - 发送网络请求
const data1 = await apiClient.get('/api/users')

// 第二次请求 - 返回缓存数据
const data2 = await apiClient.get('/api/users')

2. 请求去重

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
// 防抖和节流
function debounce(func, delay) {
  let timeoutId
  return function(...args) {
    clearTimeout(timeoutId)
    timeoutId = setTimeout(() => func.apply(this, args), delay)
  }
}

function throttle(func, delay) {
  let lastCall = 0
  return function(...args) {
    const now = Date.now()
    if (now - lastCall >= delay) {
      lastCall = now
      return func.apply(this, args)
    }
  }
}

// 请求去重器
class RequestDeduplicator {
  constructor() {
    this.pendingRequests = new Map()
  }

  async deduplicate(key, requestFn) {
    if (this.pendingRequests.has(key)) {
      return this.pendingRequests.get(key)
    }

    const promise = requestFn()
      .finally(() => {
        this.pendingRequests.delete(key)
      })

    this.pendingRequests.set(key, promise)
    return promise
  }
}

// 使用示例
const deduplicator = new RequestDeduplicator()

async function fetchUserData(userId) {
  return deduplicator.deduplicate(`user:${userId}`, async () => {
    const response = await fetch(`/api/users/${userId}`)
    return response.json()
  })
}

// 防抖搜索
const debouncedSearch = debounce(async (query) => {
  const response = await fetch(`/api/search?q=${query}`)
  return response.json()
}, 300)

// 节流滚动处理
const throttledHandleScroll = throttle((event) => {
  console.log('Scroll position:', event.target.scrollTop)
}, 100)

测试异步代码

1. 测试Promise

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// Jest测试Promise
const fetchData = () => {
  return new Promise(resolve => {
    setTimeout(() => resolve('data'), 100)
  })
}

describe('fetchData', () => {
  test('should resolve with data', async () => {
    const result = await fetchData()
    expect(result).toBe('data')
  })

  test('should handle rejection', async () => {
    const rejectFn = jest.fn()
    const errorPromise = new Promise((_, reject) => {
      rejectFn('error')
    })

    expect(errorPromise).rejects.toMatch('error')
    expect(rejectFn).toHaveBeenCalled()
  })
})

2. 测试Async/Await

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// 测试异步函数
async function processUser(userData) {
  const validatedData = await validateUser(userData)
  const savedData = await saveUser(validatedData)
  return savedData
}

const mockValidateUser = jest.fn()
const mockSaveUser = jest.fn()

jest.mock('./validation', () => ({
  validateUser: mockValidateUser
}))

jest.mock('./database', () => ({
  saveUser: mockSaveUser
}))

describe('processUser', () => {
  beforeEach(() => {
    jest.clearAllMocks()
  })

  test('should process user successfully', async () => {
    const userData = { name: 'John', email: 'john@example.com' }
    mockValidateUser.mockResolvedValue(userData)
    mockSaveUser.mockResolvedValue({ id: 1, ...userData })

    const result = await processUser(userData)

    expect(mockValidateUser).toHaveBeenCalledWith(userData)
    expect(mockSaveUser).toHaveBeenCalledWith(userData)
    expect(result).toEqual({ id: 1, ...userData })
  })

  test('should handle validation errors', async () => {
    const userData = { name: '', email: 'invalid-email' }
    mockValidateUser.mockRejectedValue(new Error('Invalid user data'))

    await expect(processUser(userData)).rejects.toThrow('Invalid user data')
  })
})

3. 测试并发控制

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// 测试并发限制器
describe('ConcurrencyLimiter', () => {
  let limiter
  let executionCount = 0

  beforeEach(() => {
    limiter = new ConcurrencyLimiter(2)
    executionCount = 0
  })

  test('should limit concurrent execution', async () => {
    const task = jest.fn().mockImplementation(async () => {
      executionCount++
      await new Promise(resolve => setTimeout(resolve, 100))
      return executionCount
    })

    const promises = [
      limiter.execute(task),
      limiter.execute(task),
      limiter.execute(task),
      limiter.execute(task),
      limiter.execute(task)
    ]

    const results = await Promise.all(promises)

    expect(results).toEqual([1, 2, 3, 4, 5])
    expect(task).toHaveBeenCalledTimes(5)
  })
})

总结

JavaScript异步编程已经从回调地狱发展到了优雅的Async/Await时代。通过掌握这些最佳实践,你可以:

核心要点

  1. 理解Promise机制:掌握Promise的三种状态和生命周期
  2. 合理使用Async/Await:简化异步代码,提高可读性
  3. 完善的错误处理:实现健壮的错误处理机制
  4. 并发控制:合理控制并发数量,避免资源耗尽
  5. 性能优化:使用缓存、去重等技术提升性能

实践建议

  • 优先使用Async/Await而不是Promise链
  • 统一错误处理策略
  • 实现合理的重试和超时机制
  • 使用缓存减少不必要的请求
  • 充分测试异步代码

JavaScript异步编程是一个不断发展的领域,保持学习和实践是掌握其精髓的关键。通过遵循这些最佳实践,你可以编写出更清晰、更可靠、更高效的异步代码。