异步操作 #

异步操作模式 #

基本模式 #

javascript
actions: {
  async fetchData({ commit }) {
    commit('SET_LOADING', true)
    
    try {
      const data = await api.fetchData()
      commit('SET_DATA', data)
      return data
    } catch (error) {
      commit('SET_ERROR', error.message)
      throw error
    } finally {
      commit('SET_LOADING', false)
    }
  }
}

完整的错误处理 #

javascript
actions: {
  async fetchUser({ commit }, userId) {
    commit('SET_LOADING', true)
    commit('CLEAR_ERROR')
    
    try {
      const response = await fetch(`/api/users/${userId}`)
      
      if (!response.ok) {
        throw new Error(`HTTP error! status: ${response.status}`)
      }
      
      const user = await response.json()
      commit('SET_USER', user)
      
      return { success: true, data: user }
    } catch (error) {
      commit('SET_ERROR', {
        message: error.message,
        timestamp: Date.now()
      })
      
      return { success: false, error: error.message }
    } finally {
      commit('SET_LOADING', false)
    }
  }
}

并发请求 #

Promise.all #

javascript
actions: {
  async fetchDashboard({ commit }) {
    commit('SET_LOADING', true)
    
    try {
      const [users, products, orders] = await Promise.all([
        api.fetchUsers(),
        api.fetchProducts(),
        api.fetchOrders()
      ])
      
      commit('SET_USERS', users)
      commit('SET_PRODUCTS', products)
      commit('SET_ORDERS', orders)
      
      return { users, products, orders }
    } catch (error) {
      commit('SET_ERROR', error.message)
      throw error
    } finally {
      commit('SET_LOADING', false)
    }
  }
}

并发控制 #

javascript
actions: {
  async fetchAllProducts({ commit }, productIds) {
    commit('SET_LOADING', true)
    
    // 限制并发数量
    const limit = 5
    const results = []
    
    for (let i = 0; i < productIds.length; i += limit) {
      const batch = productIds.slice(i, i + limit)
      const batchResults = await Promise.all(
        batch.map(id => api.fetchProduct(id))
      )
      results.push(...batchResults)
    }
    
    commit('SET_PRODUCTS', results)
    commit('SET_LOADING', false)
    
    return results
  }
}

顺序请求 #

链式调用 #

javascript
actions: {
  async fetchUserPosts({ commit }, userId) {
    commit('SET_LOADING', true)
    
    try {
      // 先获取用户
      const user = await api.fetchUser(userId)
      commit('SET_USER', user)
      
      // 再获取用户的文章
      const posts = await api.fetchUserPosts(userId)
      commit('SET_POSTS', posts)
      
      return { user, posts }
    } catch (error) {
      commit('SET_ERROR', error.message)
      throw error
    } finally {
      commit('SET_LOADING', false)
    }
  }
}

依赖请求 #

javascript
actions: {
  async fetchPostWithComments({ commit }, postId) {
    commit('SET_LOADING', true)
    
    try {
      // 获取文章
      const post = await api.fetchPost(postId)
      commit('SET_POST', post)
      
      // 根据文章作者获取作者信息
      const author = await api.fetchUser(post.authorId)
      commit('SET_AUTHOR', author)
      
      // 获取文章评论
      const comments = await api.fetchComments(postId)
      commit('SET_COMMENTS', comments)
      
      return { post, author, comments }
    } catch (error) {
      commit('SET_ERROR', error.message)
      throw error
    } finally {
      commit('SET_LOADING', false)
    }
  }
}

重试机制 #

基本重试 #

javascript
actions: {
  async fetchWithRetry({ commit }, { url, maxRetries = 3 }) {
    let lastError
    
    for (let i = 0; i < maxRetries; i++) {
      try {
        const response = await fetch(url)
        if (!response.ok) throw new Error('Request failed')
        const data = await response.json()
        commit('SET_DATA', data)
        return data
      } catch (error) {
        lastError = error
        console.log(`Retry ${i + 1}/${maxRetries}`)
        await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1)))
      }
    }
    
    commit('SET_ERROR', lastError.message)
    throw lastError
  }
}

指数退避 #

javascript
actions: {
  async fetchWithBackoff({ commit }, { url, maxRetries = 3 }) {
    let lastError
    
    for (let i = 0; i < maxRetries; i++) {
      try {
        const response = await fetch(url)
        if (!response.ok) throw new Error('Request failed')
        const data = await response.json()
        commit('SET_DATA', data)
        return data
      } catch (error) {
        lastError = error
        const delay = Math.pow(2, i) * 1000  // 1s, 2s, 4s...
        await new Promise(resolve => setTimeout(resolve, delay))
      }
    }
    
    throw lastError
  }
}

取消请求 #

使用 AbortController #

javascript
// store.js
let abortController = null

actions: {
  async fetchProducts({ commit }, filters) {
    // 取消之前的请求
    if (abortController) {
      abortController.abort()
    }
    
    abortController = new AbortController()
    commit('SET_LOADING', true)
    
    try {
      const response = await fetch('/api/products', {
        signal: abortController.signal,
        body: JSON.stringify(filters)
      })
      
      const products = await response.json()
      commit('SET_PRODUCTS', products)
      return products
    } catch (error) {
      if (error.name === 'AbortError') {
        console.log('Request cancelled')
        return
      }
      commit('SET_ERROR', error.message)
      throw error
    } finally {
      commit('SET_LOADING', false)
      abortController = null
    }
  }
}

防抖和节流 #

防抖搜索 #

javascript
let searchTimeout = null

actions: {
  async searchProducts({ commit }, query) {
    // 清除之前的定时器
    if (searchTimeout) {
      clearTimeout(searchTimeout)
    }
    
    // 防抖 300ms
    return new Promise((resolve) => {
      searchTimeout = setTimeout(async () => {
        commit('SET_SEARCH_QUERY', query)
        commit('SET_LOADING', true)
        
        try {
          const results = await api.searchProducts(query)
          commit('SET_SEARCH_RESULTS', results)
          resolve(results)
        } catch (error) {
          commit('SET_ERROR', error.message)
        } finally {
          commit('SET_LOADING', false)
        }
      }, 300)
    })
  }
}

轮询 #

定时轮询 #

javascript
let pollingInterval = null

actions: {
  startPolling({ dispatch }, { endpoint, interval = 5000 }) {
    // 停止之前的轮询
    dispatch('stopPolling')
    
    // 立即执行一次
    dispatch('fetchData', endpoint)
    
    // 设置定时轮询
    pollingInterval = setInterval(() => {
      dispatch('fetchData', endpoint)
    }, interval)
  },
  
  stopPolling() {
    if (pollingInterval) {
      clearInterval(pollingInterval)
      pollingInterval = null
    }
  },
  
  async fetchData({ commit }, endpoint) {
    try {
      const data = await api.fetch(endpoint)
      commit('SET_DATA', data)
    } catch (error) {
      console.error('Polling error:', error)
    }
  }
}

实战示例 #

完整的数据获取流程 #

javascript
// store/modules/posts.js
export default {
  namespaced: true,
  
  state: {
    items: [],
    current: null,
    pagination: {
      page: 1,
      perPage: 10,
      total: 0
    },
    loading: false,
    error: null
  },
  
  mutations: {
    SET_ITEMS(state, items) {
      state.items = items
    },
    
    APPEND_ITEMS(state, items) {
      state.items = [...state.items, ...items]
    },
    
    SET_CURRENT(state, item) {
      state.current = item
    },
    
    SET_PAGINATION(state, pagination) {
      state.pagination = { ...state.pagination, ...pagination }
    },
    
    SET_LOADING(state, loading) {
      state.loading = loading
    },
    
    SET_ERROR(state, error) {
      state.error = error
    }
  },
  
  actions: {
    // 获取列表
    async fetchList({ commit, state }, { page = 1, refresh = false } = {}) {
      if (refresh) {
        commit('SET_ITEMS', [])
      }
      
      commit('SET_LOADING', true)
      commit('SET_ERROR', null)
      
      try {
        const response = await api.getPosts({
          page,
          perPage: state.pagination.perPage
        })
        
        if (refresh || page === 1) {
          commit('SET_ITEMS', response.items)
        } else {
          commit('APPEND_ITEMS', response.items)
        }
        
        commit('SET_PAGINATION', {
          page: response.page,
          total: response.total
        })
        
        return response.items
      } catch (error) {
        commit('SET_ERROR', error.message)
        throw error
      } finally {
        commit('SET_LOADING', false)
      }
    },
    
    // 加载更多
    async loadMore({ dispatch, state }) {
      if (state.loading) return
      if (state.items.length >= state.pagination.total) return
      
      return dispatch('fetchList', {
        page: state.pagination.page + 1
      })
    },
    
    // 获取详情
    async fetchDetail({ commit }, id) {
      commit('SET_LOADING', true)
      commit('SET_ERROR', null)
      
      try {
        const post = await api.getPost(id)
        commit('SET_CURRENT', post)
        return post
      } catch (error) {
        commit('SET_ERROR', error.message)
        throw error
      } finally {
        commit('SET_LOADING', false)
      }
    },
    
    // 创建
    async create({ commit }, data) {
      commit('SET_LOADING', true)
      
      try {
        const post = await api.createPost(data)
        commit('SET_CURRENT', post)
        return post
      } catch (error) {
        commit('SET_ERROR', error.message)
        throw error
      } finally {
        commit('SET_LOADING', false)
      }
    },
    
    // 更新
    async update({ commit }, { id, data }) {
      commit('SET_LOADING', true)
      
      try {
        const post = await api.updatePost(id, data)
        commit('SET_CURRENT', post)
        return post
      } catch (error) {
        commit('SET_ERROR', error.message)
        throw error
      } finally {
        commit('SET_LOADING', false)
      }
    },
    
    // 删除
    async remove({ commit, state }, id) {
      commit('SET_LOADING', true)
      
      try {
        await api.deletePost(id)
        commit('SET_CURRENT', null)
        commit('SET_ITEMS', state.items.filter(p => p.id !== id))
      } catch (error) {
        commit('SET_ERROR', error.message)
        throw error
      } finally {
        commit('SET_LOADING', false)
      }
    }
  }
}

总结 #

异步操作要点:

模式 说明
错误处理 try/catch + finally
并发请求 Promise.all
顺序请求 await 链式调用
重试机制 循环 + 延迟
取消请求 AbortController
防抖节流 setTimeout/clearTimeout

继续学习 组合Action,了解如何组合多个 Action。

最后更新:2026-03-28