react实现类GhatGPT逐字输出效果

自己做的demo,本身也是用的GPT3.5接口

最近在捣鼓ai,对于ai,因为一般的gpt open API的速度会比较慢,而且ai的接口给到的也是一个字一个字给的,后端如果拿到所有字后再返回给前端,速度比较慢,用户体验很差

所以就需要:

1.后端采用流式传输(stream)拿到一个字就传输一个字

2.前端解析stream流,解析到一个字就渲染一个字

此处展示两种前端写法,其中只有第一种写法是可以逐字输出的

一、监听解析

首先打开一个后端的接口(使用了react的request包,如果是类umi的框架也可以使用框架封装好的request包)

import request from 'request';

const respons = request.post(`XXXXX`, { //你的后端接口
        responseType: 'stream',
        method: 'POST',
        headers: {
          'Content-Type': 'application/json; charset=utf-8',
          'Access-Control-Allow-Origin': '*',
          'Access-Control-Allow-Methods': 'GET,POST,OPTIONS',
          'appcode': '007',
        },
        body: XXXX,  //请求载荷
      })

需要设置 responseType为stream,否则会默认用json解析

此时我们创建一个post实例,需要通过监听,使得每次获取到data就setState,如果监听到end就结束解析

  const [output, setOutput] = useState<string>('')     
   respons.on('data', (data) => {
          //通过TextDecoder解析出字符串
          const chunkData = new TextDecoder().decode(data);
          //触发setState,触发渲染
          setOutput(prevState => prevState + chunkData)
        })
        respons.on('end', () => {
          //结束,相当于promise的.then()
        })


return  <Input value={output} onChange={(e) => setOutput(e.target.value)}>
</Input>

可以在end事件部分继续写请求结束之后的逻辑,比如把loading状态变为false

虽然每次监听到流式都会触发渲染,但是实测渲染速度非常快,限制渲染速度的只有接口传输速度

一般是2秒的时间请求,之后流式过来就可以一直渲染了,用户体验极好

由此,可以实现逐字输出的方法

二、完整解析(无法逐字输出)

如果不想要逐字输出,想拿到所有流式后再统一解析,可以试试这个方法

      runApp().then((res) => {
        let reader = res.body.getReader();
        let decoder = new TextDecoder();
        let chunks: any = [];
        const pump = () => {
          return reader.read().then(({ done, value }) => {
            if (done) {
              let jsonString = decoder.decode(new Uint8Array(chunks));
              let jsonObject = JSON.parse(jsonString);
              console.log(jsonObject);
              return;
            }
            chunks.push(value);
            return pump();
          });
        };
        return pump();
      })

因为使用了.then,所以接口会在所有steam传输完毕后再触发.then后面的函数

使用了getReader()去阅读接口返回的流,但是阅读是通过分段的方式去阅读的(chunks),所以需要写循环遍历函数,分段读每个chunk

该方法不会逐字输出,适合在拿到所有返回后进行统一处理再渲染

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值