@esinx/async-context

中文文档
1.0.0-2发布于 2025/09/09MITTypeScriptESM + CJS零依赖近 30 天下载 2928

Async 语境管理 TypeScript 库

中文使用文档
文档目录24 节
  1. 安装
  2. 什么是同步背景?
  3. 问题
  4. 解决办法
  5. 安装
  6. 快速启动
  7. API 参考
  8. createContext<T>(initialValue: T): ContextHandle<T>
  9. ContextHandle<T>
  10. use<T>(handle: ContextHandle<T>): T
  11. runWithContext<T>(handle: ContextHandle<T>, value: T)
  12. 常见模式
  13. 请求追查
  14. 用户背景
  15. 数据库交易
  16. 嵌入上下文
  17. 高级使用
  18. 构成
  19. 上下文提供者(类似反应模式)
  20. 最佳做法
  21. 处理错误
  22. 业绩考量
  23. 所需资源
  24. 许可证

@ esinx/ aync- context

一个用于Async上下文管理的TypeScript库,使用由Node.js Async LocalStorage提供动力的React-like上下文API.

安装

npm install @esinx/async-context
# or
yarn add @esinx/async-context
# or
pnpm add @esinx/async-context

什么是同步背景?

Async 上下文允许您在整个Async调用链条中保存上下文信息,而不明确通过每个函数参数. 这对于:

  • 请求追查: 在整个自动同步操作中维护请求ID
  • 用户背景: 在整个请求处理过程中提供用户信息
  • 日志: 在日志中自动包含背景信息
  • 数据库交易: 维护全自动同步操作的交易环境
  • 地物标志: 在整个应用程序中访问特性标记状态

问题

在传统的Async JavaScript/TypeScript代码中,通过多层的上下文可能繁琐:

// Without async context - tedious parameter passing
async function handleRequest(userId: string, requestId: string) {
    const user = await fetchUser(userId, requestId)
    const data = await processData(user, requestId)
    await saveResult(data, userId, requestId)
}

async function fetchUser(userId: string, requestId: string) {
    console.log(`[${requestId}] Fetching user ${userId}`)
    // ... fetch logic
}

async function processData(user: User, requestId: string) {
    console.log(`[${requestId}] Processing data for ${user.id}`)
    // ... processing logic
}

解决办法

在同步语境下,您可以隐含地保持此信息:

// With async context - clean and maintainable
const userContext = createContext<User | null>(null)
const requestContext = createContext<string>('')

async function handleRequest(userId: string, requestId: string) {
    await requestContext.run(requestId, async () => {
        const user = await fetchUser(userId)
        await userContext.run(user, async () => {
            const data = await processData()
            await saveResult(data)
        })
    })
}

async function fetchUser(userId: string) {
    const requestId = use(requestContext)
    console.log(`[${requestId}] Fetching user ${userId}`)
    // ... fetch logic
}

async function processData() {
    const user = use(userContext)
    const requestId = use(requestContext)
    console.log(`[${requestId}] Processing data for ${user.id}`)
    // ... processing logic
}

安装

npm install async-context
# or
yarn add async-context
# or
pnpm add async-context

快速启动

import { createContext, use } from 'async-context'

// Create a context with a default value
const userContext = createContext<string>('anonymous')

// Run code with a specific context value
await userContext.run('john-doe', async () => {
    // All async operations in this scope have access to the context
    const currentUser = use(userContext) // 'john-doe'

    await someAsyncOperation()

    // Still available after async operations
    const stillCurrentUser = use(userContext) // 'john-doe'
})

// Outside the run scope, back to default
const defaultUser = use(userContext) // 'anonymous'

API 参考

createContext<T>(initialValue: T): ContextHandle<T>

创建一个带有初始/默认值的新上下文处理.

const stringContext = createContext<string>('default')
const numberContext = createContext<number>(0)
const userContext = createContext<User | null>(null)

ContextHandle<T>

该天体由 createContext 采用下列方法:

handle.run<Result>(value: T, fn: () => Promise<Result>): Promise<Result>

运行带有特定上下文值的async函数.

const result = await handle.run('new-value', async () => {
    // Context is 'new-value' within this scope
    return await someAsyncOperation()
})

handle.use(): T

获得当前上下文值( 与独立值相同) use 函数).

const currentValue = handle.use()

use<T>(handle: ContextHandle<T>): T

从上下文控件获取当前值.

const currentUser = use(userContext)

runWithContext<T>(handle: ContextHandle<T>, value: T)

创建可重用功能,运行具有特定上下文值的操作.

const runAsAdmin = runWithContext(userContext, adminUser)

const result = await runAsAdmin(async () => {
    // This runs with adminUser as the context
    return await performAdminOperation()
})

常见模式

请求追查

import { createContext, use } from 'async-context'

const requestIdContext = createContext<string>('')

// Express middleware
app.use((req, res, next) => {
    const requestId = req.headers['x-request-id'] || generateId()
    requestIdContext.run(requestId, () => {
        next()
    })
})

// In any handler or service
async function processOrder() {
    const requestId = use(requestIdContext)
    logger.info(`Processing order`, { requestId })
    // ... processing logic
}

用户背景

interface User {
    id: string
    role: string
    permissions: string[]
}

const userContext = createContext<User | null>(null)

// Authentication middleware
async function authenticate(token: string) {
    const user = await verifyToken(token)
    return userContext.run(user, async () => {
        // All subsequent operations have access to user context
        return await handleRequest()
    })
}

// In any service
async function createPost(data: PostData) {
    const currentUser = use(userContext)
    if (!currentUser) {
        throw new Error('Not authenticated')
    }

    if (!currentUser.permissions.includes('create:post')) {
        throw new Error('Insufficient permissions')
    }

    // ... create post logic
}

数据库交易

const transactionContext = createContext<Transaction | null>(null)

async function withTransaction<T>(fn: () => Promise<T>): Promise<T> {
    const transaction = await db.beginTransaction()

    try {
        const result = await transactionContext.run(transaction, fn)
        await transaction.commit()
        return result
    } catch (error) {
        await transaction.rollback()
        throw error
    }
}

// Usage
await withTransaction(async () => {
    await createUser(userData)
    await createProfile(profileData)
    // Both operations use the same transaction automatically
})

嵌入上下文

const tenantContext = createContext<string>('default')
const userContext = createContext<User | null>(null)

await tenantContext.run('tenant-123', async () => {
    await userContext.run(user, async () => {
        // Both contexts are available
        const tenant = use(tenantContext) // 'tenant-123'
        const currentUser = use(userContext) // user object

        await performOperation()
    })
})

高级使用

构成

interface AppContext {
    user: User | null
    requestId: string
    tenant: string
}

const appContext = createContext<AppContext>({
    user: null,
    requestId: '',
    tenant: 'default',
})

// Helper function to create context
function createAppContext(user: User, requestId: string, tenant: string) {
    return { user, requestId, tenant }
}

await appContext.run(createAppContext(user, requestId, tenant), async () => {
    const { user, requestId, tenant } = use(appContext)
    // ... application logic
})

上下文提供者(类似反应模式)

class ContextProvider {
    constructor(
        private userContext = createContext<User | null>(null),
        private requestContext = createContext<string>('')
    ) {}

    async provideContext<T>(
        user: User,
        requestId: string,
        fn: () => Promise<T>
    ): Promise<T> {
        return this.requestContext.run(requestId, async () => {
            return this.userContext.run(user, fn)
        })
    }

    getCurrentUser() {
        return use(this.userContext)
    }

    getCurrentRequestId() {
        return use(this.requestContext)
    }
}

最佳做法

  1. 使用有意义的默认值: 选择适合您的应用程序的默认值
  2. 保持上下文不变: 不要修改上下文对象; 改为创建新对象
  3. 避免经常变化的数据的背景: 环境最适合相对稳定的信息
  4. 使用类型脚本: 为上下文值利用类型安全
  5. 记录您的背景: 明确每个上下文代表什么,何时可用
  6. 上下文测试: 在测试中包含上下文情景

处理错误

当错误发生时, 上下文会自动清理 :

try {
    await userContext.run(user, async () => {
        // Context is available here
        throw new Error('Something went wrong')
    })
} catch (error) {
    // Context is back to default/initial value
    const currentUser = use(userContext) // back to initial value
}

业绩考量

  • Async LocalStorage 大多数应用程序的间接费用都很少
  • 上下文搜索非常快( O(1))
  • 网格环境的额外费用极少
  • 考虑对复杂状态使用上下文构成,而不是使用许多单个背景

所需资源

  • 节点.js 16+(同步本地集成支持)
  • TypeScript 4.5+(用于最佳类型支持)

许可证

麻省理工学院

版本与发布记录

@esinx/async-context 共发布 3 个版本,首次发布于 2025/06/28;过去 12 个月发布了 0 个版本。

版本发布日期距今
1.0.0-2latest2025/09/091.2 年前
1.0.0-12025/06/281.4 年前
1.0.02025/06/281.4 年前

关于 @esinx/async-context 的常见问题

@esinx/async-context 是什么?

Async 语境管理 TypeScript 库

如何安装 @esinx/async-context?

在项目目录运行 npm install @esinx/async-context,或使用 pnpm add / yarn add / bun add。

@esinx/async-context 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@esinx/async-context 支持 ESM 还是 CommonJS?

同时提供 ES Module 与 CommonJS 入口,import 和 require() 都可以使用,打包工具会自动选择合适的版本。

@esinx/async-context 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=16。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@esinx/async-context 可以商用吗?

@esinx/async-context 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@esinx/async-context 的下载量有多少?

根据 npm 官方下载统计,@esinx/async-context 在 2026/10/14 – 2026/11/12 这 30 天内被下载 2,928 次,日均约 98 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@esinx/async-context 还在维护吗?

最新版本 1.0.0-2 发布于 2025/09/09(1.2 年前);至今共发布 3 个版本,其中过去 12 个月发布了 0 个;首次发布于 2025/06/28。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。