本文将从原生的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(拦截器统一处理认证、日志,降低协作成本)。

五、注意事项与避坑指南

  1. 域名配置:小程序必须在"微信公众平台→开发→开发设置→服务器域名"中配置合法域名,开发环境可开启"不校验合法域名"(工具→详情→本地设置),生产环境必须关闭。

  2. 兼容性:async/await需要小程序基础库版本≥2.2.0,建议在app.json中设置"miniprogramRoot": "2.2.0"

  3. Token失效处理:响应拦截器中处理401状态时,需避免重复跳转登录页(可加锁控制)。

  4. 请求取消:仅未完成的请求可取消,已到达服务器的请求无法取消,需配合后端接口支持幂等性。

  5. npm构建问题:若安装后无法引入,检查是否执行"构建npm",且项目根目录存在miniprogram_npm文件夹。

六、总结

小程序网络请求的进化,本质是异步编程范式的升级——从回调地狱到Promise链式调用,再到async/await的同步化写法,核心目标都是提升代码的可读性、可维护性。

wechat-http作为成熟的第三方库,不仅封装了Promise,更提供了拦截器、全局配置等企业级功能,配合async/await使用,能让我们在开发中彻底摆脱繁琐的异步处理,专注于业务逻辑。

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐