微信小程序 request 流式数据处理

什么是流式数据处理?

流式数据处理(Streaming Data)指逐步接收并处理数据片段的技术,无需等待全部数据加载完成。适用于大文件下载、实时日志、AI生成报告等场景,可显著降低内存占用并提升用户体验。

微信小程序中的实现方案

微信小程序通过 wx.request 的 responseType: "arraybuffer" 返回二进制数据,enableChunked 配置启用分块传输,配合 onChunkReceived 回调实现流式处理。

通用方法封装

javascript 复制代码
/**
 * 流式请求封装
 * @param {string} url 请求地址
 * @param {Object} data 请求参数
 * @param {Function} onProgress 分块数据处理回调
 * @param {Object} headers 自定义请求头
 */
export function streamRequest({ url, data, onProgress, headers }) {
  return new Promise((resolve, reject) => {
    wx.request({
      url,
      data,
      header: headers || {},
      responseType: "arraybuffer", // 关键:接收二进制流
      enableChunked: true, // 启用分块传输
      success(res) {
        if (res.statusCode === 200) resolve(res);
      },
      fail(err) {
        reject(err)
      },
      onChunkReceived(response) {
        // 每次收到分块时触发
        const chunk = response.data; // 获取二进制数据块
        try {
          // 将ArrayBuffer转为可读字符串
          const textDecoder = new TextDecoder('utf-8');
          const chunkStr = textDecoder.decode(chunk); // data:{"done":false,"response":"异常"} data:{"done":false,"response":","} data:{"done":false,"response":"建议"} data:{"done":false,"response":"结合"}
          // 根据需要做后续处理
          onProgress?.(chunkStr);
        } catch (e) {
          console.error('Chunk parsing failed:', e);
        }
      }
    });
  });
}

使用示例:AI生成报告解读实时解析

html 复制代码
<template>
  <view class="page">
    <scroll-view
      class="ai-content"
      scroll-y
      style="max-height:calc(100vh - {{navHeight}}px);"
      enable-back-to-top
      scroll-top="{{scrollTop}}"
      bindscroll="scroll"
      :scroll-with-animation="false"
      bindscrolltolower="next"
    >
      <view bindtap="getAIReportDecode">开始生成</view>
      <view>
        <van-loading wx:if="{{loading}}"/>
        <view>
          <span>报告智能解读:</span>
          <rich-text nodes="{{result}}"></rich-text>
        </view>
      </view>
    </scroll-view>
  </view>
</template>
javascript 复制代码
<script>
import { Page } from "@wxa/core";
import { streamRequest } from "../../../../static/utils/index.js";

const MarkdownIt = require('markdown-it');
const md = new MarkdownIt();

var that, $data;

@Page
export default class Index {
  data = {
    result: "",
    loading: true,
    scrollTop: 0,
    isAutoScrolling: true, // 是否自动滚动
    word: "",
    targetScrollTop: 0,
    requestTask: null
  };

  async onLoad(e) {
    that = this;
    $data = that.data;
  }

  onUnload() {
    if ($data.requestTask) {
      $data.requestTask.abort();
    }
  }
  getAIReportDecode() {
    streamRequest({
      url: "https://api.example.com/ai/report",
      data: {},
      onProgress(res) {
        const messages = res.trim().split("\n\n");
        const parsedMessages = messages
          .map(message => {
            // 按 "data:" 拆分,取后面的部分
            const dataPart = message.split("data:")[1];
            if (dataPart) {
              // 解析 JSON 字符串并返回对象
              return JSON.parse(dataPart.trim());
            }
          })
          .filter(v => {
            return v;
          }); // 过滤掉异常值
        const line = parsedMessages.map(v => {
          return v.response;
        });
        line.forEach(v => {
          $data.word += line;
          that.setData({ result: md.render($data.word) });
          that.autoScrollToBottom();
        });
      }
    }).then(() => {
      console.log("报告接收完成");
    });
  }

  scroll(e) {
    if ($data.targetScrollTop > 0) {
      return; // 忽略此次滚动事件
    }
    const { scrollTop, scrollHeight } = e.detail;
    const query = wx.createSelectorQuery();
    query
      .select(".ai-content")
      .boundingClientRect(rect => {
        if (rect) {
          const clientHeight = rect.height;
          if (scrollTop + clientHeight < scrollHeight) {
            that.setData({ isAutoScrolling: false });
          }
        }
      })
      .exec();
  }

  next() {
    that.setData({
      targetScrollTop: 100
    });
    that.setData({ isAutoScrolling: true });
    setTimeout(() => {
      that.setData({
        targetScrollTop: 0 // 重置目标 scrollTop
      });
    }, 100);
  }

  autoScrollToBottom() {
    if (!$data.isAutoScrolling) return;

    const query = wx.createSelectorQuery();
    query
      .select(".ai-content")
      .boundingClientRect(rect => {
        that.setData({
          scrollTop: rect.height * 100,
          targetScrollTop: 100
        });
        setTimeout(() => {
          that.setData({
            targetScrollTop: 0 // 重置目标 scrollTop
          });
        }, 100);
      })
      .exec();
  }
}
</script>
javascript 复制代码
<config>
{
  "usingComponents": {
    "van-loading": "@vant/weapp/loading/index"
  }
}
</config>

Markdown 解析器安装:yarn add markdown-it

相关推荐
前端小万3 天前
写公众号赚了 3000 块后,我做了一款叫 "一键成稿" 的软件
前端·微信小程序
拖孩3 天前
代码我能全交给 AI,流量主这 500 个访客它一个都替不了我
前端·后端·微信小程序
甜到心里的蛋糕3 天前
如何用企业微信实现让微信收到通知实时通知
服务器·python·微信小程序·fastapi
2601_963870203 天前
基于springboot的综合网上购物商场系统
微信小程序·小程序·课程设计
2601_963869953 天前
力序健身房管理系统
微信小程序·小程序
拖孩4 天前
给小程序加「一键发公众号贴图」,我把 wx.shareToOfficialAccount 的三个坑趟了一遍
前端·后端·微信小程序
StevenLdh5 天前
第四期 · 云端同步与冲突仲裁:离线可写、多端一致的工程解法
java·微信小程序
m0_462803885 天前
会议签到如何设置手写签名?从创建签到到导出签名 PDF 的完整流程
微信小程序
AI工具人PM产品经理6 天前
零基础微信小程序入门:一个真实项目的里程碑路线图
微信小程序·云开发·学习路线·零基础入门·ai 编程
拖孩7 天前
这个小程序是 AI 帮我写的,可它里面一个 AI 功能都没有
前端·后端·微信小程序