微信小程序网络请求进阶:告别原生的wx.request,解决回调地狱:wechat-http与async/await
本文将从原生的wx.request的痛点出发,引入第三方库wechat-http,结合Promise与async/await语法,优化升级网络请求,解决回调地狱。
一、原生wx.request
1.1 原生请求的基础使用
先看一个简单的GET请求示例,感受下原生API的使用方式:
// 原生wx.request调用示例
wx.request({
url: 'https://api.example.com/user', // 请求地址
method: 'GET', // 请求方法(默认GET)
data: { id: 123 }, // 请求参数
header: { 'Content-Type': 'application/json' }, // 请求头
timeout: 5000, // 超时时间(毫秒)
success: (res) => { // 请求成功回调
if (res.statusCode === 200) {
console.log('请求成功', res.data);
} else {
console.log('请求失败', res.statusCode);
}
},
fail: (err) => { // 请求失败回调(网络错误、超时等)
console.error('请求出错', err);
},
complete: () => { // 无论成败都会执行
console.log('请求完成');
}
});
1.2 致命痛点:嵌套依赖引发的"回调地狱"
实际开发中,我们常遇到"多步骤依赖请求"——后一个请求需要前一个请求的返回结果。比如"获取用户ID→根据ID获取订单列表→根据订单ID获取订单详情→根据详情获取物流信息",用原生API实现会是这样:
// 原生wx.request调用示例
wx.request({
url: 'https://api.example.com/user', // 请求地址
method: 'GET', // 请求方法(默认GET)
data: { id: 123 }, // 请求参数
header: { 'Content-Type': 'application/json' }, // 请求头
timeout: 5000, // 超时时间(毫秒)
success: (res) => { // 请求成功回调
if (res.statusCode === 200) {
console.log('请求成功', res.data);
} else {
console.log('请求失败', res.statusCode);
}
},
fail: (err) => { // 请求失败回调(网络错误、超时等)
console.error('请求出错', err);
},
complete: () => { // 无论成败都会执行
console.log('请求完成');
}
});
这种代码被称为"回调地狱",存在三大致命问题:
-
可读性极差:嵌套层级越深,代码越像"金字塔",后续维护者需逐层梳理逻辑。
-
错误处理繁琐:每个步骤都要单独写
fail回调,10个步骤就要写10个错误处理,冗余且易遗漏。 -
可维护性低:若需在步骤2和3之间插入新请求,需修改多层嵌套结构,极易引发BUG。
为解决这些问题,我们需要更优雅的方案——wechat-http结合Promise与async/await。
二、核心铺垫:搞懂Promise到底是什么?
在使用wechat-http前,必须先理解Promise——它是ES6引入的异步编程规范,专门用于解决回调地狱。
2.1 Promise核心特性
Promise本质是一个"异步结果容器",包含三种状态:
-
pending:初始状态,异步操作正在进行中。 -
fulfilled:成功状态,异步操作完成并返回结果。 -
rejected:失败状态,异步操作出错并返回错误信息。
关键规则:状态一旦从pending变为另外两种,就永久固定,不可逆转。
Promise的核心优势是链式调用和统一错误处理:
-
链式调用:
then()方法接收成功结果,且返回新的Promise,支持连续调用。 -
统一错误处理:末尾的
catch()可捕获所有步骤的错误,无需重复处理。
2.2 用Promise封装wx.request(简易版)
我们可以手动封装wx.request为Promise风格,理解wechat-http的底层逻辑:
// 简易Promise封装wx.request
function requestPromise(url, method = 'GET', data = {}) {
return new Promise((resolve, reject) => {
wx.request({
url,
method,
data,
header: { 'Content-Type': 'application/json' },
success: (res) => {
// 业务层面判断成功(如状态码200)
if (res.statusCode === 200) {
resolve(res.data); // 成功时传递数据
} else {
reject(new Error(`请求失败:${res.statusCode}`));
}
},
fail: (err) => {
reject(err); // 失败时传递错误
}
});
});
}
// 链式调用解决依赖问题
requestPromise('https://api.example.com/step1')
.then(res1 => {
return requestPromise(`https://api.example.com/step2?id=${res1.id}`);
})
.then(res2 => {
return requestPromise(`https://api.example.com/step3?id=${res2.id}`);
})
.then(res3 => {
return requestPromise(`https://api.example.com/step4?id=${res3.id}`);
})
.then(res4 => {
console.log('全部完成', res4);
})
.catch(error => {
console.error('任意步骤失败', error);
});
可见,Promise已将嵌套结构转为平铺的链式调用,错误处理也统一了。但这只是手动封装,实际开发中直接用成熟的第三方库wechat-http更高效。
三、实战升级:wechat-http全方位使用指南
wechat-http是基于Promise封装的小程序网络请求库,在原生API基础上扩展了拦截器、全局配置等强大功能,且完美兼容async/await。
3.1 第一步:安装与初始化
首先在小程序项目根目录执行npm安装,再通过开发者工具构建npm:
# 1. 安装wechat-http npm install wechat-http # 2. 微信开发者工具中操作:工具 → 构建npm # 3. 勾选"使用npm模块"(详情 → 本地设置)
安装完成后,在项目根目录新建utils/http.js作为全局请求工具,进行初始化配置:
// utils/http.js
import http from 'wechat-http';
// 1. 全局基础配置(一次配置,所有请求生效)
http.baseURL = 'https://api.example.com'; // 基础域名,后续请求可省略
http.timeout = 10000; // 超时时间:10秒
http.header = {
'Content-Type': 'application/json',
'X-App-Version': '1.0.0' // 自定义请求头(如APP版本)
};
// 2. 请求拦截器:发送请求前统一处理(如添加Token)
http.intercept.request = (config) => {
// 从本地缓存获取Token(登录后存储)
const token = wx.getStorageSync('userToken');
if (token) {
// 给所有请求添加Authorization头
config.header.Authorization = `Bearer ${token}`;
}
// 可修改请求配置(如动态切换域名)
// config.url = 'https://api-test.example.com' + config.url;
return config; // 必须返回配置,否则请求会中断
};
// 3. 响应拦截器:接收响应后统一处理(如错误码解析)
http.intercept.response = (res) => {
// 响应格式:{ statusCode: 200, data: { code: 0, msg: "成功", data: {} } }
const { statusCode, data } = res;
// 网络层面成功(状态码200)
if (statusCode === 200) {
// 业务层面判断:假设code=0为成功,其他为错误
if (data.code === 0) {
return data.data; // 直接返回业务数据,简化上层使用
} else {
// 业务错误:抛出错误信息(会被catch捕获)
wx.showToast({ title: data.msg, icon: 'none' });
return Promise.reject(new Error(data.msg));
}
} else if (statusCode === 401) {
// 未授权:跳转登录页并清除Token
wx.removeStorageSync('userToken');
wx.navigateTo({ url: '/pages/login/login' });
return Promise.reject(new Error('登录已过期,请重新登录'));
} else {
// 其他网络错误
wx.showToast({ title: `请求失败:${statusCode}`, icon: 'none' });
return Promise.reject(new Error(`网络错误:${statusCode}`));
}
};
// 4. 导出http实例,供全局使用
export default http;
配置完成后,在app.js中全局挂载,方便所有页面直接使用:
// app.js
import http from './utils/http';
App({
onLaunch() {
// 全局挂载http请求工具
this.globalData.http = http;
},
globalData: {
http: null,
userInfo: null
}
});
3.2 核心用法:GET/POST与async/await结合
async/await是ES2017引入的语法糖,基于Promise实现,能让异步代码写法完全同步化。结合wechat-http,可彻底告别链式调用,写出极具可读性的代码。
以"多步骤依赖请求"为例,实现如下:
// pages/demo/demo.js
const app = getApp();
const http = app.globalData.http; // 获取全局http实例
Page({
data: {
logisticsInfo: null // 最终物流信息
},
onLoad() {
// 调用异步任务
this.doMultiStepRequest();
},
// 异步任务:多步骤依赖请求
async doMultiStepRequest() {
// 显示加载中
wx.showLoading({ title: '加载中...' });
try {
// 步骤1:获取用户ID(无需写完整URL,自动拼接baseURL)
const userId = await http.get('/user/getId');
// 步骤2:根据用户ID获取订单ID
const orderId = await http.get('/order/getId', { userId }); // 传参
// 步骤3:根据订单ID获取订单详情
const orderDetail = await http.post('/order/detail', { orderId }); // POST请求
// 步骤4:根据详情中的物流ID获取物流信息
const logisticsInfo = await http.get(`/logistics/get?logId=${orderDetail.logId}`);
// 所有步骤成功:更新页面数据
this.setData({ logisticsInfo });
} catch (error) {
// 任意步骤失败:捕获错误(响应拦截器已处理提示,这里可做额外逻辑)
console.error('任务执行失败', error);
} finally {
// 无论成败:关闭加载中
wx.hideLoading();
}
}
});
这段代码的优势一目了然:
-
同步化逻辑:从上到下顺序执行,和"步骤1→步骤2→步骤3→步骤4"的自然逻辑完全一致。
-
错误集中处理:
try/catch捕获所有步骤错误,配合响应拦截器的提示,体验更友好。 -
简洁高效:无需嵌套和链式调用,代码量减少50%以上,维护成本极低。
3.3 高级功能:取消请求与请求配置覆盖
wechat-http还支持取消请求(如用户快速切换页面时)和局部配置覆盖(单个请求特殊配置):
// 1. 取消请求示例
Page({
data: {
requestTask: null // 存储请求实例
},
onLoad() {
// 发起请求并保存实例
this.data.requestTask = http.get('/largeData');
},
// 页面卸载时取消请求
onUnload() {
const { requestTask } = this.data;
if (requestTask) {
requestTask.cancel('页面已卸载,取消请求');
console.log('请求已取消');
}
}
});
// 2. 局部配置覆盖示例(单个请求特殊超时和请求头)
async getSpecialData() {
const data = await http.get('/specialData', {
// 局部参数(会覆盖全局配置)
timeout: 20000, // 该请求超时时间20秒
header: {
'Content-Type': 'application/x-www-form-urlencoded'
}
});
}
四、三种方案对比与选型建议
为帮助大家根据场景选择合适方案,整理对比表格如下:
|
对比维度 |
原生wx.request |
wechat-http+Promise |
wechat-http+async/await |
|---|---|---|---|
|
语法风格 |
回调函数(success/fail) |
.then()链式调用 |
同步代码风格 |
|
可读性 |
极差(嵌套层级深) |
较好(平铺结构) |
极佳(同步逻辑) |
|
错误处理 |
每个步骤单独处理 |
末尾.catch()统一处理 |
try/catch统一处理 |
|
功能扩展 |
无(需手动封装) |
支持拦截器、全局配置 |
支持所有扩展功能 |
|
适用场景 |
1-2层简单请求 |
3-5层依赖请求 |
任意复杂度(推荐) |
选型建议:
-
小型demo或简单请求:原生
wx.request(无需额外依赖)。 -
中型项目或多依赖请求:
wechat-http+async/await(平衡效率与可读性)。 -
大型项目或团队协作:必须用
wechat-http(拦截器统一处理认证、日志,降低协作成本)。
五、注意事项与避坑指南
-
域名配置:小程序必须在"微信公众平台→开发→开发设置→服务器域名"中配置合法域名,开发环境可开启"不校验合法域名"(工具→详情→本地设置),生产环境必须关闭。
-
兼容性:async/await需要小程序基础库版本≥2.2.0,建议在app.json中设置
"miniprogramRoot": "2.2.0"。 -
Token失效处理:响应拦截器中处理401状态时,需避免重复跳转登录页(可加锁控制)。
-
请求取消:仅未完成的请求可取消,已到达服务器的请求无法取消,需配合后端接口支持幂等性。
-
npm构建问题:若安装后无法引入,检查是否执行"构建npm",且项目根目录存在
miniprogram_npm文件夹。
六、总结
小程序网络请求的进化,本质是异步编程范式的升级——从回调地狱到Promise链式调用,再到async/await的同步化写法,核心目标都是提升代码的可读性、可维护性。
wechat-http作为成熟的第三方库,不仅封装了Promise,更提供了拦截器、全局配置等企业级功能,配合async/await使用,能让我们在开发中彻底摆脱繁琐的异步处理,专注于业务逻辑。
更多推荐
所有评论(0)