uni-app中借助Promise封装异步请求 uni-app优雅处理API调用

作者:袖梨 2026-06-16
Promise封装是必须的,关键在于统一拦截、错误分流、自动token注入和loading管理;原生uni.request为回调式API,需封装解决嵌套深、难调试、无法await及错误处理分散等问题。

直接结论:Promise封装是必须的,但不能只套一层 Promise.resolve() 就算完事;关键在于统一拦截、错误分流、自动 token 注入和 loading 状态管理。

uni.request 为什么要用 Promise 封装?

原生 uni.request 是回调式 API,嵌套深、难调试、无法 await、错误处理分散。不封装就写业务,等于裸写异步逻辑——每次都要手动写 success/failcatch 无处落脚,loading 开关靠人肉维护。

封装的核心价值不是“看起来更现代”,而是解决四个实际问题:

  • 避免重复写 uni.showLoading() / uni.hideLoading()
  • 401404、网络超时等错误归类处理,而不是每页都弹一遍模态框
  • 自动读取并携带 token,避免每个接口都手动拼 header.Authorization
  • api/user/info 这种调用能直接 await,配合 async setup() 写法更自然

封装时 header 和 method 的常见陷阱

很多人封装后 POST 请求失败,返回 415 Unsupported Media Type,问题就出在 header 配置上。

注意以下三点:

  • method 大小写敏感:"POST" 不等于 "post",uni-app 只认小写字符串
  • Content-Type 必须按请求体类型匹配:传 data: {a:1} 且是 POST 时,应设 {'Content-Type': 'application/json'};若传的是 FormData,则必须用 'multipart/form-data',且不能手动设 —— uni.request 会自动处理 boundary
  • 小程序平台(如微信)对 header 字段名强制小写,Authorization 写成 authorization 也能通,但为保兼容,统一用首字母大写标准写法

示例中容易错写成:

if (method === 'post') {  header['Content-Type'] = 'application/x-www-form-urlencoded' // ❌ 错!没序列化 data,后端收不到}

正确做法是:需要表单提交时,显式用 encodeURIComponent 拼接,或改用 uni.uploadFile 处理文件上传。

如何让封装支持 async/await 且不崩

Promise 封装本身不难,难的是在 failcomplete 中正确 reject / resolve,并确保异常可捕获。

关键点:

  • fail 回调里必须 reject(err),不能只 console.log;否则 await api.xxx().catch(...) 永远进不去 catch
  • complete 里不能做 resolvereject,它只负责清理(比如关 loading),否则会覆盖 success/fail 的结果
  • 网络异常(如断网)触发的 failerr.errMsg"request:fail network error" 这类字符串,不能直接当 HTTP 状态码用
  • 务必在 try/catch 外层包一层 .catch(() => {}),防止未捕获的 Promise rejection 导致白屏(尤其在 Vue 3 setup 中)

最小可用封装骨架:

export default function request(params) {  uni.showLoading({ title: '加载中' })  return new Promise((resolve, reject) => {    uni.request({      url: BASE_URL + params.url,      method: (params.method || 'GET').toLowerCase(),      header: {        ...params.header,        ...(params.method?.toUpperCase() === 'POST' ? { 'Content-Type': 'application/json' } : {}),        Authorization: uni.getStorageSync('token') ? `Bearer ${uni.getStorageSync('token')}` : ''      },      data: params.data,      success: res => {        if (res.statusCode >= 200 && res.statusCode < 300) {          resolve(res.data)        } else {          reject(new Error(`HTTP ${res.statusCode}`))        }      },      fail: err => reject(err),      complete: () => uni.hideLoading()    })  })}

为什么你封装的 API 在某些页面里 await 不生效

现象:在 onLoad 里写 await api.getUser(),控制台报 Cannot read property 'then' of undefined

根本原因只有两个:

  • 封装函数没 return Promise(比如忘了写 return new Promise(...),或者在条件分支里漏了 return)
  • 调用时没加 await,却误以为函数本身会阻塞执行(JavaScript 异步本质不会变)

更隐蔽的问题是:Vue 2 选项式 API 中,data 初始化早于 onLoad,如果你在 data() 里就调用了封装函数,那它一定还没挂载完成,this 上下文为空,uni.getStorageSync 可能拿不到值 —— 这类问题不会报错,但 token 总是空。

建议始终把 API 调用放在生命周期钩子(onLoadonShow)或事件回调中,而非 datacomputed 里。

相关文章

精彩推荐