前言
近期忙于将公司内部的AI产品接入到小程序中
调研
发现通过调用内部AI接口发现接口返回的格式是event stream的形式,而小程序则是通过开启wx.request中的enableChunked的方式来实现
关于wx.request的官方说明
关于Event Stream(SSE)
相比于SSE之前更熟悉Websocket,那么两种有什么区别呢?
- SSE本质上还是属于http协议,而Websocket并不属于http协议
- SSE是单向通信,只能由服务端向客户端发送请求。
- 以及SSE是通过保持一个持久的 HTTP 连接来实现推送,属于长连接,而Websocket是全双工的。
在微信小程序中如何处理SSE
1. 在小程序中相应的API
前文已经提到,核心在于配置wx.request中的enableChunked参数
const requestTask = wx.request({
url,
method: 'POST',
data: params,
enableChunked: true,
header: {
token,
}
})
微信官方并且提供了监听与取消的API
//监听的api
requestTask.onChunkReceived((res) => {
//xxxxxxxxxx
});
//取消的api
requestTask.offChunkReceived()
2.关于如何处理onChunkReceived回调的结果
可以看到返回的结果为Uint8Array,而在小程序中由于不支持TextEncoder/TextDecoder对象 我的解决方式
- 先将unit8Array转化为十六进制
- 将十六进制转为中文字符串
其中需要引入encoding.js和encoding-indexes.js两个文件
这两个文件的来源为text-encoding下lib文件下的两个文件。
import encoding from './encoding.js'
decodeUint8Array(uint8Array) {
const data16 = this.buf2hex(uint8Array);
const requestData = this.hexToStr(data16)
return requestData
},
buf2hex(arrayBuffer) {
return Array.prototype.map.call(new Uint8Array(arrayBuffer), x => ('00' + x.toString(16)).slice(-2)).join('');
},
/**
* 十六进制字符串转中文
* @param {String} hex 为十六进制字符串
* @return {String} 包含中文的字符串
*/
hexToStr(hex) {
// 去掉字符串首尾空格
let trimedStr = hex.trim()
// 判断trimedStr前两个字符是否为0x,如果是则截取从第三个字符及后面所有,否则返回全部字符
let rawStr = trimedStr.substr(0, 2).toLowerCase() === "0x" ? trimedStr.substr(2) : trimedStr
// 得到rawStr的长度
let len = rawStr.length
// 如果长度不能被2整除,那么传入的十六进制值有误,返回空字符
if (len % 2 !== 0) {
return ""
}
let curCharCode // 接收每次循环得到的字符
let resultStr = [] // 存转换后的十进制值数组
for (let i = 0; i < len; i = i + 2) {
curCharCode = parseInt(rawStr.substr(i, 2), 16)
resultStr.push(curCharCode)
}
// encoding为空时默认为utf-8
let bytesView = new Uint8Array(resultStr) // 8 位无符号整数值的类型化数组
let str = new encoding.TextDecoder().decode(bytesView)
return str
},
最终调用的伪代码,其中的chunkText为处理好的字符串。
requestTask.onChunkReceived((res) => {
let chunkText = this.decodeUint8Array(res.data);
});
tips:不知道为什么在微信开发者工具的控制台中,即使接口响应成功还是看不到SSE的返回,如下图,只能通过console的方式来查看返回的数据。
如何实现打字机效果
在我们熟悉的大多AI产品中,它们的回答都是逐字出现的,这种效果我们也称为打字机效果 打字机的核心代码
startTyping(text) {
const strLength = text.length;
let currentIndex = 0;
const intervalTime = 100;
let msg = '';
let typingInterval = setInterval(() => {
if (currentIndex < strLength) {
//获取当前索引对应的字
let currentChar = text.charAt(currentIndex);
msg += currentChar
//将msg赋值给你需要的数据项即可
this.setData({
test:msg
});
currentIndex++;
} else {
clearInterval(typingInterval);
}
}, intervalTime);
},