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

直接结论:Promise封装是必须的,但不能只套一层 Promise.resolve() 就算完事;关键在于统一拦截、错误分流、自动 token 注入和 loading 状态管理。
uni.request 为什么要用 Promise 封装?
原生 uni.request 是回调式 API,嵌套深、难调试、无法 await、错误处理分散。不封装就写业务,等于裸写异步逻辑——每次都要手动写 success/fail,catch 无处落脚,loading 开关靠人肉维护。
封装的核心价值不是“看起来更现代”,而是解决四个实际问题:
- 避免重复写
uni.showLoading()/uni.hideLoading() - 把
401、404、网络超时等错误归类处理,而不是每页都弹一遍模态框 - 自动读取并携带
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 封装本身不难,难的是在 fail 和 complete 中正确 reject / resolve,并确保异常可捕获。
关键点:
-
fail回调里必须reject(err),不能只console.log;否则await api.xxx().catch(...)永远进不去catch -
complete里不能做resolve或reject,它只负责清理(比如关 loading),否则会覆盖success/fail的结果 - 网络异常(如断网)触发的
fail,err.errMsg是"request:fail network error"这类字符串,不能直接当 HTTP 状态码用 - 务必在
try/catch外层包一层.catch(() => {}),防止未捕获的 Promise rejection 导致白屏(尤其在 Vue 3setup中)
最小可用封装骨架:
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 调用放在生命周期钩子(onLoad、onShow)或事件回调中,而非 data 或 computed 里。


















