首页
/ Redux 基础教程实战:用中间件与 Redux Thunk 实现异步逻辑和数据获取

Redux 基础教程实战:用中间件与 Redux Thunk 实现异步逻辑和数据获取

2026-09-04 20:58:46作者:申梦珏Efrain

本篇内容基于 Redux 官方基金教程第 6 篇(part-6-async-logic.md),讲解当应用数据不再只存在于客户端、而需要通过与服务器进行 HTTP 请求来读写时,Redux 是如何通过**中间件(middleware)**与 Thunk 函数来承载异步逻辑的:Redux 数据流在引入异步后会多出哪些环节、为什么 reducer 中不能写副作用、applyMiddleware 在源码层面如何把多个中间件组装成一条 dispatch 管线,以及如何用 thunk 函数从服务端拉取待办列表并保存新待办。读完本篇,你将能够配置带 thunk 中间件的 store、编写"接收 dispatchgetState 的异步函数"、在组件中 dispatch thunk 完成真实的数据获取流程,并在源码与测试层面验证这套机制的每个环节。

Redux 异步数据流示意图

前置知识与你将学到的内容

本教程第 6 篇建立在 第 5 篇:UI 和 React 的基础上:你已经会用 react-redux 让组件与 Redux store 交互——调用 useSelector 读取状态、调用 useDispatch 拿到 dispatch 函数、并用 <Provider> 组件让 hooks 访问到 store。

本篇学习目标:

  • Redux 数据流在处理异步数据时是如何工作的;
  • 如何用 Redux 中间件编写异步逻辑;
  • 处理异步请求状态(request state)的模式。

前置要求:

  • 熟悉使用 HTTP 请求从服务器获取和更新数据;
  • 理解 JS 中的异步逻辑,包括 Promise。

值得先了解的一点:Redux Toolkit 提供了专门的数据获取与缓存方案 RTK Query,它可以消除为数据获取编写任何 thunk 或 reducer 的需要。Redux 官方教程将 RTK Query 作为数据获取的默认推荐方案,而 RTK Query 正是建立在本篇讲解的同一套模式之上,参见 RTK Query 概述Redux Essentials, Part 7: RTK Query Basics。本篇讲的是这些底层机制本身。

示例 REST API 与 HTTP 客户端

为了让示例项目既隔离又贴近现实,教程配套的示例应用(redux-fundamentals-example-app)在初始搭建时就内置了一个内存版的假 REST API(使用 Mirage.js 这个 mock API 工具配置)。该 API 以 /fakeApi 作为所有端点的基础 URL,并对 /fakeApi/todos 支持常见的 GET/POST/PUT/DELETE HTTP 方法,定义在示例应用的 src/api/server.js 中。

项目还包含一个小型 HTTP API 客户端对象,暴露 client.get()client.post() 方法,用法类似 axios 等常见 HTTP 库,定义在示例应用的 src/api/client.js 中。本节的 HTTP 调用都通过这个 client 对象发往内存中的假 REST API。

Redux 中间件与副作用

单看一个 Redux store,它对异步逻辑一无所知:它只会同步地 dispatch 动作、通过调用根 reducer 函数更新状态,并通知 UI 发生了变化。任何异步性都必须发生在 store 之外。

在前面的教程中我们说过,Redux reducer 绝不能包含"副作用"。"副作用"指的是任何能够被函数返回值之外的外部世界观察到的状态或行为改变。常见的副作用包括:

  • 向控制台打印日志;
  • 保存文件;
  • 设置异步定时器;
  • 发起 HTTP 请求;
  • 修改函数外部存在的某个状态,或直接改变(mutate)函数参数;
  • 生成随机数或唯一随机 ID(如 Math.random()Date.now())。

然而,任何真实应用都总要在某个地方做这些事情。既然 reducer 里不能放副作用,那副作用应该放在哪里

Redux 中间件正是为了让你可以编写带副作用的逻辑而设计的。

正如 第 4 篇 中所述,Redux 中间件在"看到"一个被 dispatch 的动作时可以做任何事:打印日志、修改动作、延迟动作、发起异步调用,等等。而且,由于中间件构成了包裹在真实 store.dispatch 函数外的一整条管线,这意味着你实际上可以向 dispatch 传递不是一个纯 action 对象的值——只要某个中间件拦截了这个值、并且不让它一路到达 reducer 就行。

中间件还能访问 dispatchgetState。也就是说,你可以在中间件里编写异步逻辑,并且依然能够通过 dispatch 动作与 Redux store 保持交互。

Redux 作者在 StackOverflow 上多次解释过"为什么异步流需要中间件",其核心论点与本节一致:状态更新必须同步、可预测,而副作用必须被移出 reducer 到 dispatch 管线上。

用中间件启用异步逻辑

下面看两个例子,展示中间件如何让我们编写能与 Redux store 交互的异步逻辑。

一种可能是写一个查找特定 action 类型的中间件,在"看到"这些动作时运行异步逻辑:

import { client } from '../api/client'

const delayedActionMiddleware = storeAPI => next => action => {
  if (action.type === 'todos/todoAdded') {
    setTimeout(() => {
      // 延迟这个 action 一秒
      next(action)
    }, 1000)
    return
  }

  return next(action)
}

const fetchTodosMiddleware = storeAPI => next => action => {
  if (action.type === 'todos/fetchTodos') {
    // 发起 API 调用,从服务器拉取 todos
    client.get('todos').then(todos => {
      // 用接收到的 todos dispatch 一个 action
      storeAPI.dispatch({ type: 'todos/todosLoaded', payload: todos })
    })
  }

  return next(action)
}

注意这两个中间件的写法:都是"三层嵌套函数"——最外层接收 storeAPI,中间层接收 next,最内层接收 action。这与本仓库 src/types/middleware.tsMiddleware 类型的定义完全对应:

// src/types/middleware.ts(节选)
export interface MiddlewareAPI<D extends Dispatch = Dispatch, S = any> {
  dispatch: D
  getState: () => S
}

export interface Middleware<
  _DispatchExt = {},
  S = any,
  D extends Dispatch = Dispatch
> {
  (
    api: MiddlewareAPI<D, S>
  ): (next: (action: unknown) => unknown) => (action: unknown) => unknown
}

也就是说,中间件的契约就是 (api) => (next) => (action) => 任意返回值,其中最内层函数的参数类型是 unknown——这从类型层面就承认了:dispatch 进来的一切都不保证是普通 action 对象,拦截非对象值(比如函数)正是中间件的合法职责。

源码视角:applyMiddleware 如何组装中间件管线

以上"三层嵌套函数"最终是由 applyMiddleware 串成管线的。看本仓库的实现 src/applyMiddleware.ts

export default function applyMiddleware(...middlewares: Middleware[]): StoreEnhancer<any> {
  return createStore => (reducer, preloadedState) => {
    const store = createStore(reducer, preloadedState)
    let dispatch: Dispatch = () => {
      throw new Error(
        'Dispatching while constructing your middleware is not allowed. ' +
          'Other middleware would not be applied to this dispatch.'
      )
    }

    const middlewareAPI: MiddlewareAPI = {
      getState: store.getState,
      dispatch: (action, ...args) => dispatch(action, ...args)
    }
    const chain = middlewares.map(middleware => middleware(middlewareAPI))
    dispatch = compose<typeof dispatch>(...chain)(store.dispatch)

    return {
      ...store,
      dispatch
    }
  }
}

从这段源码可以读出几个关键事实:

  1. applyMiddleware 本身是一个 store enhancer:它返回 createStore => (reducer, preloadedState) => store 这样的高阶函数,最终返回一个"覆盖了 dispatch 的新 store"。这也解释了教程第 4 篇中"中间件是构建在一个非常特殊的内建 store enhancer 之上的"这句话。函数注释中还特别提示:由于中间件可能是异步的,applyMiddleware 应当是 enhancer 组合链中的第一个。
  2. 每个中间件拿到的 dispatch 是"指向管线入口"的递归引用middlewareAPI.dispatch 调用的是闭包中会被重新赋值的 dispatch 变量,最终被赋值为组合后的完整管线。因此中间件内部再 dispatch 的动作,会从第一个中间件重新走完整条管线,而不会被漏掉。
  3. 管线的组装发生在构建期middlewares.map(middleware => middleware(middlewareAPI)) 先对每个中间件调用最外层函数,拿到各自的 wrapDispatch;再 compose(...chain)(store.dispatch) 把它们从右向左嵌套,把原始的 store.dispatch 作为链尾(即最后一个中间件的 next)。compose 的实现见 src/compose.ts,即 compose(f, g, h) === (...args) => f(g(h(...args)))——这决定了 applyMiddleware(a, b) 时,a 是管线最外层、最先执行,b 最后执行并紧邻真实 store.dispatch
  4. 构建期禁止 dispatch:在中间件装配完成之前,dispatch 被临时指向一个会抛出 "Dispatching while constructing your middleware is not allowed" 的函数(src/applyMiddleware.ts),防止你在中间件外层函数里 dispatch 时绕过尚未装配的管线。

这些行为都有测试背书,见 test/applyMiddleware.spec.ts

  • warns when dispatching during middleware setupL9-L20)验证了第 4 点的抛错行为;
  • wraps dispatch method with middleware onceL22-L45)验证中间件外层函数只被调用一次,且拿到的 API 上确实有 getStatedispatch
  • passes recursive dispatches through the middleware chainL47-L65)验证"中间件内部 dispatch 的动作会重新经过整条管线"。

编写一个"异步函数"中间件

上一节的两个中间件都太具体,各只干一件事。我们当然希望有一种办法:把任意异步逻辑提前写成一个独立函数(与中间件定义解耦),同时这个函数还能访问 dispatchgetState 来与 store 交互。

如果我们写一个允许向 dispatch 传一个"函数"(而不是 action 对象)的中间件呢? 这个中间件检查一下"动作"是否其实是函数,如果是,就立刻调用它。这样异步逻辑就能写在中间件定义之外的独立函数里。这样的中间件大概长这样:

// 示例异步函数中间件
const asyncFunctionMiddleware = storeAPI => next => action => {
  // 如果这个"action"其实是个函数……
  if (typeof action === 'function') {
    // 那就调用它,并把 `dispatch` 和 `getState` 作为参数传入
    return action(storeAPI.dispatch, storeAPI.getState)
  }

  // 否则,它是一个普通 action——继续往下传
  return next(action)
}

有了它,我们就可以这样使用:

const middlewareEnhancer = applyMiddleware(asyncFunctionMiddleware)
const store = createStore(rootReducer, middlewareEnhancer)

// 写一个以 `dispatch` 和 `getState` 为参数的函数
const fetchSomeData = (dispatch, getState) => {
  // 发起一次异步 HTTP 请求
  client.get('todos').then(todos => {
    // 用接收到的 todos dispatch 一个 action
    dispatch({ type: 'todos/todosLoaded', payload: todos })
    // dispatch 之后读取更新后的 store 状态
    const allTodos = getState().todos
    console.log('Number of todos after loading: ', allTodos.length)
  })
}

// 把上面写的"函数"传给 `dispatch`
store.dispatch(fetchSomeData)
// 日志输出:'Number of todos after loading: ###'

注意:这个"异步函数中间件"让我们向 dispatch 传入了一个函数。在函数内部,我们先写了一段异步逻辑(HTTP 请求),请求完成后再 dispatch 一个普通的 action 对象。

这里有一个容易忽略的边界:如果没有中间件拦截,把函数传给 dispatch 会在 store 的原始 dispatch 里直接失败。从源码看,src/createStore.ts 中对 action 的校验会检查 action.type 是否为字符串,非普通对象的值根本无法通过 reducer 校验流程;仓库也提供了 src/utils/isAction.ts 这样的工具函数,其判定标准就是"是纯对象且 type 为字符串"。换言之,函数型 dispatch 之所以可行,完全依赖于中间件先于 store 内部逻辑拦截了它——这正是文档所说"只要中间件拦截该值、不让它到达 reducer"的底层依据。

本仓库的测试辅助代码里就有一个与上面 asyncFunctionMiddleware 几乎一模一样的最小 thunk 实现,见 test/helpers/middleware.ts

export const thunk: Middleware<{
  <R>(thunk: (dispatch: Dispatch, getState: () => any) => R): R
}> =
  ({ dispatch, getState }) =>
  next =>
  action =>
    typeof action === 'function' ? action(dispatch, getState) : next(action)

test/applyMiddleware.spec.ts 中名为 works with thunk middleware 的测试用例,就用它验证了完整闭环:dispatch 一个 thunk 函数后,thunk 内部 dispatch 的普通 action 能正确更新 store 状态。

Redux 的异步数据流

那么,中间件和异步逻辑对 Redux 应用的整体数据流有什么影响?

和普通 action 一样,我们首先要处理一个用户事件(比如点击按钮),然后调用 dispatch(),传入某种东西——一个纯 action 对象、一个函数,或其他可以被中间件查找识别的值。

这个被 dispatch 的值到达中间件后,中间件可以发起一次异步调用,等异步调用完成后再 dispatch 一个真正的 action 对象。

第 2 篇 中,我们见过表示常规同步 Redux 数据流的示意图。当 Redux 应用加入异步逻辑后,就多了中间件运行 HTTP 请求等逻辑、再 dispatch action 的额外环节。异步数据流看起来就是文章开头那张图:UI 事件触发 dispatch,值进入中间件管线,中间件在异步等待(HTTP 请求、定时器等)结束后 dispatch 真实 action,action 再经过剩余中间件到达 reducer,状态更新后通知 UI 重新渲染。

使用 Redux Thunk 中间件

实际上,Redux 官方早就有了那个"异步函数中间件"的标准版本,叫做 Redux "Thunk" 中间件(npm 包 redux-thunk)。thunk 中间件让我们编写以 dispatchgetState 为参数的函数。thunk 函数内部可以包含我们想要的任何异步逻辑,这些逻辑可以按需 dispatch action、读取 store 状态。

把异步逻辑写成 thunk 函数,让我们可以在事先不知道将使用哪个 Redux store 的情况下复用这些逻辑。

术语说明:"thunk" 是一个编程术语,指"一段执行延迟工作(delayed work)的代码"。更多用法可参考 Writing Logic with Thunks 使用指南。

配置 Store

Redux thunk 中间件作为 npm 包 redux-thunk 发布,需要先安装:

npm install redux-thunk

安装后,更新 todo 应用的 Redux store 以使用这个中间件:

import { createStore, applyMiddleware } from 'redux'
// highlight-next-line
import { thunk } from 'redux-thunk'
import { composeWithDevTools } from 'redux-devtools-extension'
import rootReducer from './reducer'

// highlight-next-line
const composedEnhancer = composeWithDevTools(applyMiddleware(thunk))

// store 现在具备了在 `dispatch` 中接受 thunk 函数的能力
const store = createStore(rootReducer, composedEnhancer)
export default store

composeWithDevTools(applyMiddleware(thunk)) 正是把 applyMiddleware 返回的 enhancer 交给 DevTools 组合函数再传给 createStore,对应 src/applyMiddleware.ts 中 "enhancer 包 enhancer" 的高阶结构。

从服务器获取 Todos

现在我们的 todo 条目只能存在于客户端浏览器里。我们首先需要一种方式,在应用启动时从服务器加载待办列表。

先写一个 thunk 函数:它发起 HTTP 调用请求 /fakeApi/todos 端点、取得 todo 对象数组,然后 dispatch 一个以该数组为 payload 的 action。由于这与 todos 功能整体相关,thunk 函数写在 todosSlice.js 里(示例应用文件):

import { client } from '../../api/client'

const initialState = []

export default function todosReducer(state = initialState, action) {
  // 省略 reducer 逻辑
}

// Thunk 函数
// highlight-start
export async function fetchTodos(dispatch, getState) {
  const response = await client.get('/fakeApi/todos')
  dispatch({ type: 'todos/todosLoaded', payload: response.todos })
}
// highlight-end

这个 API 调用只希望在应用第一次加载时执行一次。可以放的地方有好几处:

  • <App> 组件的 useEffect hook 里;
  • <TodoList> 组件的 useEffect hook 里;
  • 直接在 index.js 里,紧跟在导入 store 之后。

这里先试放在 index.js 里:

import React from 'react'
import { createRoot } from 'react-dom/client'
import { Provider } from 'react-redux'
import './index.css'
import App from './App'

import './api/server'

// highlight-start
import store from './store'
import { fetchTodos } from './features/todos/todosSlice'

store.dispatch(fetchTodos)
// highlight-end

const root = createRoot(document.getElementById('root'))

root.render(
  <React.StrictMode>
    <Provider store={store}>
      <App />
    </Provider>
  </React.StrictMode>
)

刷新页面后,UI 上没有可见变化。但如果打开 Redux DevTools 扩展,应该能看到一个 'todos/todosLoaded' 动作被 dispatch 了,其中包含由我们的假服务器 API 生成的一些 todo 对象:

DevTools 中 todosLoaded 动作的内容

注意:虽然我们已经 dispatch 了动作,但状态并没有发生任何变化。我们需要在 todos reducer 中处理这个动作,状态才会更新。

给 reducer 加一个 case 来把数据加载进 store。因为数据是从服务器取来的,我们要完全替换已有的 todos,所以直接返回 action.payload 数组,让它成为 todos 的新 state 值:

import { client } from '../../api/client'

const initialState = []

export default function todosReducer(state = initialState, action) {
  switch (action.type) {
    // 省略其他 reducer case
    // highlight-start
    case 'todos/todosLoaded': {
      // 直接返回新值,整体替换现有 state
      return action.payload
    }
    // highlight-end
    default:
      return state
  }
}

export async function fetchTodos(dispatch, getState) {
  const response = await client.get('/fakeApi/todos')
  dispatch({ type: 'todos/todosLoaded', payload: response.todos })
}

由于 dispatch 一个动作会立刻更新 store,我们也可以在 thunk 里调用 getState,在 dispatch 之后读取更新后的状态值。例如,在 dispatch 'todos/todosLoaded' 动作前后各打印一次 todo 总数:

export async function fetchTodos(dispatch, getState) {
  const response = await client.get('/fakeApi/todos')

  // highlight-next-line
  const stateBefore = getState()
  console.log('Todos before dispatch: ', stateBefore.todos.length)

  dispatch({ type: 'todos/todosLoaded', payload: response.todos })

  // highlight-next-line
  const stateAfter = getState()
  console.log('Todos after dispatch: ', stateAfter.todos.length)
}

这正体现了中间件 API 中 getState 的价值:从 src/applyMiddleware.ts 可以看到,middlewareAPI.getState 直接引用了 store 的 getState,因此 thunk 内部读到的永远是当下最新的状态。

保存 Todo 条目

接下来,每当创建新待办条目时,我们也需要更新服务器。正确做法是:不要立即 dispatch 'todos/todoAdded' 动作,而是先向服务器发起一次携带初始数据的 API 调用,等待服务器返回新保存的 todo 条目副本,然后再用这个 todo 条目 dispatch 动作。

但如果直接把它写成一个 thunk 函数,会立刻遇到一个问题:thunk 是写在 todosSlice.js 里的独立函数,发起 API 调用的代码并不知道新 todo 的文本是什么

async function saveNewTodo(dispatch, getState) {
  // ❌ 我们需要新 todo 的文本,但它从哪来?
  // highlight-next-line
  const initialTodo = { text }
  const response = await client.post('/fakeApi/todos', { todo: initialTodo })
  dispatch({ type: 'todos/todoAdded', payload: response.todo })
}

我们需要一种方式:写一个接收 text 参数的函数,由它创建真正的 thunk 函数,使得 thunk 能利用 text 值发起 API 调用。外层函数返回这个 thunk 函数,让我们可以把它传给组件里的 dispatch

// 写一个同步的外层函数,接收 `text` 参数:
export function saveNewTodo(text) {
  // 然后创建并返回异步 thunk 函数:
  return async function saveNewTodoThunk(dispatch, getState) {
    // ✅ 现在可以用 text 值并把它发送到服务器
    const initialTodo = { text }
    const response = await client.post('/fakeApi/todos', { todo: initialTodo })
    dispatch({ type: 'todos/todoAdded', payload: response.todo })
  }
}

现在可以在 <Header> 组件中使用它:

import React, { useState } from 'react'
import { useDispatch } from 'react-redux'

// highlight-next-line
import { saveNewTodo } from '../todos/todosSlice'

const Header = () => {
  const [text, setText] = useState('')
  const dispatch = useDispatch()

  const handleChange = e => setText(e.target.value)

  const handleKeyDown = e => {
    // 如果用户按下了回车键:
    const trimmedText = text.trim()
    if (e.which === 13 && trimmedText) {
      // highlight-start
      // 用用户输入的文本创建 thunk 函数
      const saveNewTodoThunk = saveNewTodo(trimmedText)
      // 然后把 thunk 函数本身 dispatch 出去
      dispatch(saveNewTodoThunk)
      // highlight-end
      setText('')
    }
  }

  // 省略渲染输出
}

由于我们清楚自己会立刻把 thunk 函数传给组件中的 dispatch,可以省去那个临时变量:直接调用 saveNewTodo(text),把得到的 thunk 函数原样传给 dispatch

const handleKeyDown = e => {
  // 如果用户按下了回车键:
  const trimmedText = text.trim()
  if (e.which === 13 && trimmedText) {
    // highlight-start
    // 创建 thunk 函数并立刻 dispatch
    dispatch(saveNewTodo(trimmedText))
    // highlight-end
    setText('')
  }
}

此时组件实际上并不知道自己在 dispatch 一个 thunk 函数——saveNewTodo 函数封装了真正发生的事情。<Header> 组件只知道:用户按回车时,它需要 dispatch 某个值

这种"写一个函数来准备将要传给 dispatch 的东西"的模式,就是所谓的 "action creator" 模式,在 Part 7: 标准 Redux 模式 中会进一步展开。

现在可以看到更新后的 'todos/todoAdded' 动作被 dispatch:

DevTools 中异步 todoAdded 动作的内容

最后需要修改的是 todos reducer。当我们向 /fakeApi/todos 发起 POST 请求时,服务器会返回一个全新的 todo 对象(包含新的 ID 值)。这意味着 reducer 不必自己计算新 ID、也不必填其他字段——它只需要构造一个包含新 todo 条目的新 state 数组

const initialState = []

export default function todosReducer(state = initialState, action) {
  switch (action.type) {
    // highlight-start
    case 'todos/todoAdded': {
      // 返回一个新的 todos 状态数组,新 todo 条目追加在末尾
      return [...state, action.payload]
    }
    // highlight-end
    // 省略其他 case
    default:
      return state
  }
}

这样,新增 todo 就完全正常工作了,状态 diff 如下:

DevTools 中异步 todoAdded 动作的状态 diff

提示:thunk 函数既能用于异步逻辑,也能用于同步逻辑。thunk 提供了一种方式来编写任何需要访问 dispatchgetState 的可复用逻辑。

本教程小结

到目前为止,我们已经成功更新了 todo 应用:可以用 "thunk" 函数向假服务器 API 发起 HTTP 请求,从而获取待办列表并保存新的待办条目。

在这个过程中,我们看到了 Redux 中间件是如何让我们发起异步调用、并在异步调用完成后通过 dispatch 动作与 store 交互的。核心结论回顾:

  • Redux 中间件被设计用于编写带副作用的逻辑
    • "副作用"是改变函数外部状态/行为的代码,例如 HTTP 请求、修改函数参数、生成随机值;
  • 中间件在标准 Redux 数据流中加了一个额外环节
    • 中间件可以拦截被传给 dispatch 的其他值;
    • 中间件可以访问 dispatchgetState,因此可以在异步逻辑中 dispatch 更多动作;
  • Redux "Thunk" 中间件让我们可以向 dispatch 传函数
    • "thunk" 函数让我们可以提前写好异步逻辑,而无需事先知道将使用哪个 Redux store;
    • Redux thunk 函数接收 dispatchgetState 作为参数,可以 dispatch "已从 API 响应中收到这些数据"之类的动作。

从本仓库源码看,这套机制的骨架非常紧凑:applyMiddlewaresrc/applyMiddleware.ts)以 enhancer 形式把中间件链组合成新的 dispatchMiddleware/MiddlewareAPI 类型(src/types/middleware.ts)定义了三层嵌套函数契约与 dispatch/getState 两个能力,composesrc/compose.ts)完成从右到左的管线嵌套,而 test/applyMiddleware.spec.ts 中的用例则逐一验证了构建期禁 dispatch、中间件仅装配一次、内部 dispatch 重走管线、以及与 thunk 配合的完整行为。

下一步

到这里,我们已经覆盖了使用 Redux 的所有核心部分:

  • 编写根据 dispatch 的动作更新状态的 reducer;
  • 用 reducer、enhancer 和中间件创建并配置 Redux store;
  • 使用中间件编写会 dispatch 动作的异步逻辑。

Part 7: 标准 Redux 模式 中,我们将研究真实 Redux 应用常用的若干代码模式,让代码更一致,并随应用增长更好地扩展。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
527
590
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
904
1.82 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
docsdocs
暂无描述
Markdown
889
5.78 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.52 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.33 K
1.45 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
980
502
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384