深入Effect核心:Effect数据类型与错误处理机制

深入Effect核心:Effect数据类型与错误处理机制

【免费下载链接】effect A fully-fledged functional effect system for TypeScript with a rich standard library 【免费下载链接】effect 项目地址: https://gitcode.com/GitHub_Trending/ef/effect

本文深入解析了TypeScript功能式编程框架Effect的核心数据类型与错误处理机制。Effect<R, E, A>的三重类型参数系统提供了强大的类型安全性,其中R代表环境依赖类型(逆变),E代表错误类型(协变),A代表成功值类型(协变)。文章详细介绍了每个参数的特性和实际应用,包括环境依赖的注入机制和错误类型的层次结构。此外,还探讨了Effect强大的结构化错误处理系统Cause,这是一个代数数据类型,能够完整记录失败过程的所有细节,包括并行错误、顺序错误、缺陷和中断等,确保了错误信息的完整性和可追溯性。

Effect数据类型的三重类型参数解析

Effect作为TypeScript功能式编程框架的核心数据类型,其设计哲学体现在其精妙的三重类型参数系统上。这个系统不仅提供了强大的类型安全性,还为开发者提供了对程序行为的细粒度控制。让我们深入解析Effect<R, E, A>这三个类型参数的含义和作用。

类型参数结构概览

Effect类型采用三个泛型参数来定义其完整的行为特征:

interface Effect<out A, out E = never, out R = never>

为了更好地理解这三个参数的关系,我们可以用以下流程图来表示Effect类型的结构:

mermaid

第一个参数A:成功值的类型

参数A代表Effect计算成功时返回的值类型。这是Effect的核心产出,也是我们最关心的结果。

特性:

  • 协变(covariant):如果B是A的子类型,那么Effect<B, E, R>是Effect<A, E, R>的子类型
  • 默认必须指定,不能省略
  • 可以是任何TypeScript类型

示例:

// 返回字符串的Effect
const stringEffect: Effect<string> = Effect.succeed("hello")

// 返回数字的Effect  
const numberEffect: Effect<number> = Effect.succeed(42)

// 返回复杂对象的Effect
interface User {
  id: number
  name: string
  email: string
}
const userEffect: Effect<User> = Effect.succeed({
  id: 1,
  name: "Alice",
  email: "alice@example.com"
})

第二个参数E:错误类型

参数E代表Effect计算可能失败的错误类型。这个参数提供了强大的错误处理能力。

特性:

  • 协变(covariant):错误类型的子类型关系会传递到Effect类型
  • 默认值为never,表示不会失败
  • 可以是任何类型,包括自定义错误类型

错误类型的层次结构:

mermaid

示例:

// 不会失败的Effect(E = never)
const safeEffect: Effect<string> = Effect.succeed("safe")

// 可能抛出字符串错误的Effect
const stringErrorEffect: Effect<string, string> = 
  Effect.fail("something went wrong")

// 使用自定义错误类型
class ValidationError {
  constructor(readonly field: string, readonly message: string) {}
}
const validationEffect: Effect<void, ValidationError> = 
  Effect.fail(new ValidationError("email", "Invalid format"))

// 联合错误类型
type AppError = ValidationError | DatabaseError | NetworkError
const appEffect: Effect<string, AppError> = // ...

第三个参数R:环境依赖类型

参数R代表Effect执行所需的环境依赖类型。这是Effect最具特色的功能之一,实现了依赖注入的类型安全。

特性:

  • 逆变(contravariant):环境要求的超类型关系
  • 默认值为never,表示不需要任何环境
  • 使用Context.Tag来定义和访问环境服务

环境依赖的工作机制:

mermaid

示例:

// 不需要环境的Effect
const noEnvEffect: Effect<string> = Effect.succeed("no environment needed")

// 需要Logger环境的Effect
interface Logger {
  log(message: string): void
}
const Logger = Context.Tag<Logger>()
const loggerEffect: Effect<void, never, Logger> = 
  Effect.flatMap(Logger, logger => Effect.sync(() => logger.log("Hello")))

// 需要多个环境的Effect
interface Database {
  query(sql: string): Promise<any>
}
interface Config {
  port: number
  host: string
}

const Database = Context.Tag<Database>()
const Config = Context.Tag<Config>()

const multiEnvEffect: Effect<any, Error, Database | Config> = 
  Effect.gen(function*() {
    const db = yield* Database
    const config = yield* Config
    return yield* Effect.tryPromise(() => 
      db.query(`SELECT * FROM users WHERE host = '${config.host}'`)
    )
  })

三重参数的交互关系

三个类型参数不是孤立的,它们共同定义了Effect的完整行为特征。下表总结了它们的相互作用:

参数含义默认值变型重要性
A成功值类型必须指定协变核心产出
E错误类型never协变错误处理
R环境依赖never逆变依赖注入

复杂类型的推导示例:

// 复杂的Effect类型示例
type ComplexEffect = Effect<
  User[],                    // 成功时返回用户数组
  DatabaseError | AuthError, // 可能抛出数据库或认证错误
  Database | Logger | Config // 需要数据库、日志和配置服务
>

// 类型推导在实际代码中的应用
function getUserById(id: number): Effect<User, DatabaseError, Database> {
  return Effect.gen(function*() {
    const db = yield* Database
    const result = yield* Effect.tryPromise({
      try: () => db.query(`SELECT * FROM users WHERE id = ${id}`),
      catch: (error) => new DatabaseError(error.message)
    })
    return result[0]
  })
}

类型参数的实践意义

理解这三个类型参数对于有效使用Effect框架至关重要:

  1. 类型安全:编译器能够检查所有可能的错误路径和环境依赖
  2. 文档作用:类型签名本身就是最好的文档,清晰表达了函数的契约
  3. 重构友好:类型系统会在修改时提示所有需要更新的地方
  4. 测试便利:可以轻松模拟环境依赖进行单元测试

通过掌握Effect的三重类型参数系统,开发者可以构建出既安全又灵活的函数式TypeScript应用程序,充分利用类型系统的强大能力来减少运行时错误和提高代码质量。

结构化错误处理与Cause系统

Effect框架的核心优势之一是其强大的结构化错误处理机制,通过Cause系统实现了无信息丢失的错误追踪。传统的错误处理往往只关注最终的错误结果,而Effect的Cause系统则完整记录了整个失败过程的所有细节,包括并行错误、顺序错误、缺陷和中断等。

Cause数据结构解析

Effect的Cause<E>类型是一个代数数据类型(ADT),包含以下几种主要变体:

type Cause<E> = 
  | Empty        // 无错误
  | Fail<E>      // 预期的业务错误
  | Die          // 未预期的缺陷(缺陷)
  | Interrupt    // 纤维中断
  | Sequential<E> // 顺序组合的错误
  | Parallel<E>  // 并行组合的错误

每种变体都承载着特定的语义信息,让我们通过一个表格来详细了解:

Cause变体描述使用场景示例
Empty表示没有错误发生成功执行的EffectEffect.succeed(42)
Fail<E>预期的业务逻辑错误验证失败、业务规则违反Effect.fail(new ValidationError())
Die未预期的程序缺陷空指针、类型错误等运行时异常throw new Error("Unexpected")
Interrupt纤维被主动中断超时、用户取消操作Effect.interrupt
Sequential<E>顺序操作中的错误组合链式操作中的多个错误effect1.flatMap(_ => effect2)
Parallel<E>并行操作中的错误组合并行执行时的多个错误Effect.all([eff1, eff2])

Cause的层次结构

Cause系统采用树状结构来组织错误信息,这使得复杂的错误场景能够被精确建模。让我们通过mermaid流程图来理解Cause的构建过程:

mermaid

错误处理操作符

Effect提供了丰富的操作符来处理和转换Cause,这些操作符可以分为几个主要类别:

诊断操作符

  • cause - 提取Effect的完整Cause
  • mapErrorCause - 转换Cause中的错误类型
  • sandbox - 将Effect转换为包含Cause的Either

恢复操作符

  • catchAll - 捕获所有错误并尝试恢复
  • catchSome - 捕获特定类型的错误
  • orElse - 提供备选方案

过滤操作符

  • filterOrFail - 条件过滤,失败时返回指定错误
  • filterOrDie - 条件过滤,失败时抛出缺陷

实际应用示例

让我们通过一个用户注册的场景来展示Cause系统的强大功能:

import { Effect, Cause } from "effect"

class ValidationError {
  readonly _tag = "ValidationError"
  constructor(readonly message: string) {}
}

class DatabaseError {
  readonly _tag = "DatabaseError" 
  constructor(readonly message: string) {}
}

// 用户注册流程
const registerUser = (userData: any) =>
  Effect.gen(function* () {
    // 验证用户数据
    yield* validateUserData(userData)
    
    // 检查用户是否存在
    yield* checkUserExists(userData.email)
    
    // 创建用户
    yield* createUser(userData)
    
    return "用户注册成功"
  })

// 验证函数
const validateUserData = (data: any) =>
  data.email && data.password 
    ? Effect.unit 
    : Effect.fail(new ValidationError("邮箱和密码不能为空"))

// 检查用户是否存在
const checkUserExists = (email: string) =>
  Math.random() > 0.5
    ? Effect.fail(new DatabaseError("用户已存在"))
    : Effect.unit

// 创建用户
const createUser = (data: any) =>
  Math.random() > 0.8
    ? Effect.dieMessage("数据库连接失败")
    : Effect.succeed({ id: 1, ...data })

// 错误处理
const handleRegistration = registerUser({ email: "test@example.com" }).pipe(
  Effect.catchAll(error => {
    // 获取完整的Cause信息
    return Effect.gen(function* () {
      const cause = yield* Effect.cause
      
      // 分析错误类型
      if (Cause.isFailType(cause)) {
        return `业务错误: ${error.message}`
      } else if (Cause.isDieType(cause)) {
        return `系统缺陷: 请联系管理员`
      } else {
        return `未知错误: ${Cause.pretty(cause)}`
      }
    })
  })
)

Cause的调试和日志

Effect提供了强大的调试工具来分析和可视化Cause结构:

// 打印详细的Cause信息
const logCauseDetails = (cause: Cause<unknown>) => {
  console.log("错误摘要:", Cause.pretty(cause))
  console.log("错误深度:", Cause.size(cause))
  console.log("是否包含中断:", Cause.isInterrupted(cause))
  console.log("缺陷数量:", Cause.defects(cause).length)
}

// 转换Cause为结构化JSON
const causeToJSON = (cause: Cause<unknown>) =>
  Cause.reduceWithContext(cause, null, {
    emptyCase: () => ({ type: "Empty" }),
    failCase: (_, error) => ({ type: "Fail", error }),
    dieCase: (_, defect) => ({ type: "Die", defect }),
    interruptCase: (_, fiberId) => ({ type: "Interrupt", fiberId }),
    sequentialCase: (_, left, right) => ({ 
      type: "Sequential", 
      left: causeToJSON(left), 
      right: causeToJSON(right) 
    }),
    parallelCase: (_, left, right) => ({ 
      type: "Parallel", 
      left: causeToJSON(left), 
      right: causeToJSON(right) 
    })
  })

高级错误处理模式

对于复杂的应用程序,我们可以建立分层的错误处理策略:

// 错误处理层
const withErrorHandling = <E, A>(effect: Effect.Effect<A, E>) =>
  effect.pipe(
    Effect.catchAll(error => {
      // 第一层:业务错误恢复
      if (error instanceof ValidationError) {
        return Effect.succeed("验证失败,请检查输入")
      }
      
      // 第二层:基础设施错误
      if (error instanceof DatabaseError) {
        return Effect.succeed("系统繁忙,请稍后重试")
      }
      
      // 第三层:未知错误上报
      return Effect.gen(function* () {
        const cause = yield* Effect.cause
        yield* reportErrorToMonitoring(cause)
        return "系统异常,已通知管理员"
      })
    }),
    Effect.catchAllDefect(defect => {
      // 处理未预期缺陷
      return Effect.succeed("系统发生严重错误")
    })
  )

// 错误上报
const reportErrorToMonitoring = (cause: Cause<unknown>) =>
  Effect.sync(() => {
    // 将结构化错误信息发送到监控系统
    const errorInfo = {
      timestamp: new Date().toISOString(),
      cause: causeToJSON(cause),
      stack: new Error().stack
    }
    console.error("监控上报:", errorInfo)
  })

通过这种结构化的错误处理方式,开发者能够获得完整的错误上下文,而不是零散的错误信息片段。Cause系统确保了错误信息的完整性和可追溯性,为调试和监控提供了强大的支持。

Effect的Cause系统代表了错误处理的新范式,它将错误从简单的值提升为包含丰富上下文信息的数据结构。这种设计使得构建健壮、可维护的应用程序变得更加容易,特别是在复杂的并发和分布式场景中。

Effect执行模型与运行时环境

Effect的执行模型构建在轻量级纤程(Fiber)架构之上,提供了强大的并发控制和资源管理能力。整个运行时环境经过精心设计,既保证了函数式编程的纯粹性,又提供了卓越的性能表现。

纤程(Fiber)架构

Effect的核心执行单元是纤程(Fiber),这是一种轻量级的并发原语,类似于操作系统线程但更加高效。每个Effect程序都在纤程中执行,纤程之间可以并发运行,并通过结构化并发机制进行管理。

mermaid

纤程的状态机模型确保了执行的安全性和可预测性:

mermaid

运行时标志(RuntimeFlags)系统

Effect的运行时行为通过一套精细的运行时标志系统进行控制,这些标志允许开发者根据需求调整执行策略:

运行时标志描述默认状态
Interruption控制纤程是否可被中断启用
OpSupervision操作级监控,用于性能分析禁用
RuntimeMetrics运行时指标收集禁用
WindDown纤程结束时的清理模式禁用
CooperativeYielding协作式任务调度启用
// 运行时标志配置示例
import { Runtime, RuntimeFlags } from "effect"

// 创建自定义运行时配置
const customRuntime = Runtime.make({
  context: Context.empty(),
  runtimeFlags: RuntimeFlags.make(
    RuntimeFlags.Interruption,
    RuntimeFlags.CooperativeYielding
  ),
  fiberRefs: FiberRefs.empty()
})

// 动态修改运行时标志
const optimizedRuntime = Runtime.updateRuntimeFlags(
  customRuntime,
  flags => RuntimeFlags.enable(flags, RuntimeFlags.RuntimeMetrics)
)

调度器(Scheduler)体系

Effect提供了多层次的调度器系统,支持不同的执行策略和优先级控制:

mermaid

调度器的主要实现包括:

  1. MixedScheduler:混合策略调度器,根据任务数量自动选择同步或异步执行
  2. SyncScheduler:同步调度器,立即执行所有任务
  3. ControlledScheduler:可控调度器,支持手动步进执行
// 调度器使用示例
import { Scheduler, Effect } from "effect"

// 创建自定义调度器
const customScheduler = Scheduler.make(
  (task, priority) => {
    // 自定义任务调度逻辑
    if (priority > 5) {
      setTimeout(task, 0) // 高优先级异步执行
    } else {
      task() // 低优先级同步执行
    }
  },
  (fiber) => {
    // 自定义yield决策逻辑
    return fiber.currentOpCount > 1000 ? 1 : false
  }
)

// 使用自定义调度器执行Effect
const program = Effect.sync(() => console.log("Running with custom scheduler"))
Effect.runFork(program, { scheduler: customScheduler })

执行策略(ExecutionStrategy)

Effect提供了灵活的执行策略来控制并发行为:

import { Effect, ExecutionStrategy } from "effect"

// 顺序执行策略
const sequential = Effect.all([task1, task2, task3], {
  strategy: ExecutionStrategy.sequential
})

// 完全并行执行策略  
const parallel = Effect.all([task1, task2, task3], {
  strategy: ExecutionStrategy.parallel
})

// 限制并发度的并行执行
const parallelN = Effect.all([task1, task2, task3], {
  strategy: ExecutionStrategy.parallelN(2) // 最多2个并发
})

运行时执行模式

Effect支持多种执行模式,适应不同的应用场景:

同步执行模式
// 同步执行,直接返回结果或抛出异常
const result: number = Effect.runSync(program)

// 同步执行并返回Exit类型
const exit: Exit.Exit<number, Error> = Effect.runSyncExit(program)
异步回调模式
// 异步回调执行
Effect.runCallback(program, {
  onExit: (exit) => {
    if (exit._tag === "Success") {
      console.log("Success:", exit.value)
    } else {
      console.log("Failure:", exit.effect_instruction_i0)
    }
  }
})
Promise集成模式
// 返回Promise的执行
const promise: Promise<number> = Effect.runPromise(program)

// 返回包含Exit的Promise
const promiseExit: Promise<Exit.Exit<number, Error>> = Effect.runPromiseExit(program)

纤程本地存储(FiberRefs)

Effect提供了纤程本地存储机制,类似于线程本地存储但更加安全:

import { FiberRef, Effect } from "effect"

// 创建纤程本地引用
const traceIdRef = FiberRef.make("unknown-trace-id")

// 设置和获取纤程本地值
const program = Effect.gen(function*() {
  yield* FiberRef.set(traceIdRef, "request-123")
  const traceId = yield* FiberRef.get(traceIdRef)
  console.log("Trace ID:", traceId) // 输出: Trace ID: request-123
})

结构化并发管理

Effect的执行模型建立在结构化并发原则之上,确保资源的安全管理和生命周期的可控性:

mermaid

这种结构确保了当父纤程被中断时,所有子纤程也会被正确清理,防止资源泄漏和僵尸进程。

性能优化特性

Effect运行时包含多项性能优化措施:

  1. 快速路径优化:对简单操作(如纯值、Option、Either等)进行特殊处理
  2. 操作计数:跟踪每个纤程的操作次数,实现公平调度
  3. 内存池:重用纤程和相关数据结构,减少内存分配
  4. 批处理:对多个任务进行批处理执行,提高缓存利用率
// 快速路径优化示例
const fastResult = Effect.runSync(Effect.succeed(42)) // 直接返回,无需纤程创建
const fastError = Effect.runSync(Effect.fail("error")) // 直接抛出,无需纤程创建

Effect的执行模型和运行时环境经过精心设计,既提供了强大的并发控制能力,又保持了函数式编程的纯粹性和可组合性。通过纤程架构、运行时标志系统、灵活的调度器和执行策略,开发者可以构建出既安全又高性能的应用程序。

同步与异步操作的统一处理

Effect框架最强大的特性之一是其对同步和异步操作的统一抽象处理。在传统的JavaScript/TypeScript开发中,开发者需要区分同步函数调用、Promise异步操作、回调函数等不同的执行模式。Effect通过统一的Effect数据类型,将这些不同的执行模式抽象为一致的编程接口,极大地简化了复杂应用的开发。

统一的Effect数据类型

Effect使用单一的数据类型Effect<A, E, R>来表示所有类型的操作:

  • A: 操作成功时的返回值类型
  • E: 操作失败时的错误类型
  • R: 操作所需的环境依赖类型

这种统一的类型系统使得无论是同步计算、异步Promise、还是复杂的并发操作,都可以用相同的方式组合和处理。

同步操作处理

对于纯粹的同步计算,Effect提供了sync构造函数:

import { Effect } from "effect"

// 同步计算 - 简单的数学运算
const syncCalculation = Effect.sync(() => {
  const a = 10
  const b = 20
  return a + b
})

// 同步操作 - 读取本地配置
const readConfig = Effect.sync(() => {
  return { timeout: 5000, retries: 3 }
})

sync构造函数接收一个同步函数,将其包装为Effect类型。这种方式确保了即使是最简单的同步操作也能享受到Effect的错误处理、依赖注入等高级特性。

异步Promise集成

Effect通过promise构造函数无缝集成JavaScript的Promise:

import { Effect } from "effect"

// 将Promise转换为Effect
const fetchUserData = Effect.promise(() => 
  fetch('/api/user').then(res => res.json())
)

// 支持AbortSignal的异步操作
const asyncOperation = Effect.promise((signal) => {
  return new Promise((resolve, reject) => {
    signal.addEventListener('abort', () => {
      reject(new Error('Operation aborted'))
    })
    
    setTimeout(() => {
      resolve('Operation completed')
    }, 1000)
  })
})

promise构造函数自动处理Promise的成功和失败状态,将其转换为Effect的成功和失败通道,同时支持AbortSignal用于操作取消。

高级异步控制

对于更复杂的异步场景,Effect提供了asyncasyncEffect构造函数:

import { Effect } from "effect"

// 使用async处理回调式API
const asyncCallback = Effect.async<number, Error>((resume) => {
  someCallbackApi((error, result) => {
    if (error) {
      resume(Effect.fail(error))
    } else {
      resume(Effect.succeed(result))
    }
  })
})

// 使用asyncEffect进行复杂的异步控制
const complexAsync = Effect.asyncEffect<string, Error>((resume) => {
  return Effect.gen(function*() {
    // 执行一些前置操作
    const token = yield* getAuthToken()
    
    // 注册回调
    registerCallback((result) => {
      resume(Effect.succeed(result))
    })
    
    return Effect.sync(() => cleanupResources())
  })
})

统一的执行接口

无论操作是同步还是异步,Effect都提供统一的执行接口:

执行方法用途返回值
runSync同步执行Effect直接返回值A
runPromise异步执行EffectPromise
runSyncExit同步执行并返回ExitExit<A, E>
runPromiseExit异步执行并返回ExitPromise<Exit<A, E>>
import { Effect } from "effect"

// 同步执行
const syncResult = Effect.runSync(syncCalculation)
console.log(syncResult) // 30

// 异步执行
const asyncResult = await Effect.runPromise(fetchUserData)
console.log(asyncResult) // 用户数据

// 带错误处理的执行
const exitResult = Effect.runSyncExit(riskyOperation)
if (exitResult._tag === 'Success') {
  console.log('成功:', exitResult.value)
} else {
  console.log('失败:', exitResult.cause)
}

执行模式的选择策略

Effect根据操作的性质智能选择执行策略:

mermaid

错误处理的统一性

同步和异步操作在错误处理上完全一致:

import { Effect, Cause } from "effect"

const operation = Effect.gen(function*() {
  // 同步错误
  const config = yield* readConfig()
  
  // 异步操作
  const data = yield* fetchUserData()
  
  return { config, data }
})

// 统一的错误处理
const result = await Effect.runPromiseExit(operation)
if (result._tag === 'Failure') {
  console.error('操作失败:', Cause.pretty(result.cause))
} else {
  console.log('操作成功:', result.value)
}

性能优化机制

Effect在底层实现了智能的执行优化:

  1. 同步操作快速路径:纯同步操作直接执行,无额外开销
  2. 异步操作调度:使用Fiber系统进行高效的异步调度
  3. 资源管理:自动管理异步操作的资源生命周期
  4. 取消支持:统一的取消机制适用于所有操作类型

实际应用示例

下面是一个结合同步和异步操作的完整示例:

import { Effect, Schedule, Console } from "effect"

// 同步验证函数
const validateInput = (input: string) =>
  Effect.sync(() => {
    if (input.length < 3) {
      throw new Error('Input too short')
    }
    return input.toUpperCase()
  })

// 异步API调用
const apiCall = (data: string) =>
  Effect.promise(() =>
    fetch('/api/process', {
      method: 'POST',
      body: JSON.stringify({ data })
    }).then(res => res.json())
  )

// 组合操作
const processInput = (input: string) =>
  Effect.gen(function*() {
    // 同步验证
    const validated = yield* validateInput(input)
    
    // 异步API调用(带重试机制)
    const result = yield* apiCall(validated).pipe(
      Effect.retry(Schedule.recurs(3))
    )
    
    return result
  })

// 统一执行
const main = async () => {
  try {
    const result = await Effect.runPromise(processInput('hello'))
    Console.log('处理结果:', result)
  } catch (error) {
    Console.error('处理失败:', error)
  }
}

这种统一的处理方式使得开发者可以专注于业务逻辑,而不需要关心底层的执行细节,大大提高了代码的可维护性和可读性。

总结

Effect框架通过精妙的三重类型参数系统和强大的Cause错误处理机制,为TypeScript开发者提供了前所未有的类型安全性和错误处理能力。R、E、A三个类型参数共同定义了Effect的完整行为特征,实现了依赖注入的类型安全和细粒度的错误控制。Cause系统作为错误处理的新范式,将简单的错误值提升为包含丰富上下文信息的数据结构,支持复杂的错误诊断和恢复策略。结合纤程架构的运行时环境和统一的同步异步处理模型,Effect使得构建健壮、可维护且高性能的应用程序变得更加容易。这些特性特别适合复杂的并发和分布式场景,代表了现代TypeScript函数式编程的最佳实践。

【免费下载链接】effect A fully-fledged functional effect system for TypeScript with a rich standard library 【免费下载链接】effect 项目地址: https://gitcode.com/GitHub_Trending/ef/effect

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值