来实现一个ChatGPT!!
动手实现一个简化版的 ChatGPT。对,没错,就是那个神奇的聊天机器人。 话不多说, 直接开整,让它在屏幕上跳舞!
分析
首先是一闪一闪的光标, chatGPT是使用伪元素实现的。以及blink
的动画效果
我们来看看chatGPT是如何做到一点一点接收返回的消息, 并且有时候还会直接断掉,显示networkerror, 这里一开始想的是用的websocket,但是控制台一看,如下图。并不是ws, 而是使用了fetchAPI的
ReadableStream
特性
在这里使用它的好处就是,可以实时的获取信息, 不必等chatgpt分析完整个请求再一次性的返回, 不然等待时间过长,体验太差。
不过值得一提的是New Bing
使用的就是ws来实时获取信息的。
"ws" 是 WebSocket 的缩写,而 "event stream" 是指服务器发送事件(Server-Sent Events,SSE)。WebSocket 是一种双向通信协议,旨在在 Web 应用程序中提供实时的数据交换。它使用标准的 HTTP 协议作为握手阶段,随后将 HTTP 连接升级为 WebSocket 连接。WebSocket 连接是一种持久连接,可以在客户端和服务器之间实时传输数据,而不需要进行轮询或定时轮询。WebSocket 可以使用任何数据格式(如 JSON、XML 或二进制数据)进行通信。 相反,Server-Sent Events 是一种仅由服务器向客户端发送数据的单向通信协议。服务器使用标准的 HTTP 协议作为握手阶段,但不升级连接。服务器随后使用 HTTP 连接向客户端发送事件流。这些事件可以是任何格式(如 JSON、XML 或文本),并且事件流通常用于向客户端提供实时更新,例如股票报价、天气预报等。 因此,WebSocket 和 Server-Sent Events 都提供了在 Web 应用程序中实时交换数据的机制,但它们的实现方式略有不同,WebSocket 提供双向通信,而 Server-Sent Events 仅提供单向通信。
实现
服务端
后端部分使用nodejs实现,比较简单
// server.js
const http = require('http');
const server = http.createServer((req, res) => {
if (req.url === '/event-stream') {
// 设置跨域头
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Connection', 'keep-alive');
res.flushHeaders();
// 发送事件
let counter = 0;
const interval = setInterval(() => {
counter++;
res.write(`hello!`); // 发送数据
if (counter >= 10) {
clearInterval(interval);
res.end();
}
}, 1000);
} else {
res.writeHead(404);
res.end();
}
});
const PORT = 3000;
server.listen(PORT, () => {
console.log(`Server is running on port ${PORT}`);
});
用setInterval来模拟服务端缓慢的数据处理~~~
前端
首先是基本的样式, 这里就用div来模拟光标
<div id="input-container">
<span id="text-container"></span>
<span id="cursor"></span>
</div>
#input-container {
display: flex;
align-items: center;
}
#cursor {
display: inline-block;
background-color: black;
width: 8px;
height: 1em;
animation: blink 1s infinite;
}
@keyframes blink {
0% {
opacity: 1;
}
50% {
opacity: 0;
}
100% {
opacity: 1;
}
}
然后是js部分
<script type="module">
const textContainer = document.getElementById('text-container');
async function fetchEventStream() {
const response = await fetch('http://localhost:3000/event-stream');
if (response.ok) {
const reader = response.body.getReader(); //主要是这里
const decoder = new TextDecoder('utf-8');
while (true) {
const {value, done} = await reader.read();
if (done) {
console.log('发送完毕!');
break;
}
const data = decoder.decode(value);
console.log(`接收到的: ${data}`);
textContainer.textContent += data;
}
} else {
console.error('Failed to fetch event stream.');
}
}
fetchEventStream();
</script>
完成!效果如图:
至于怎么变成真的ChatGPT, 哈哈, 当然是接入调参了
最后
一般ReadableStream
可以用来处理以下几种情况
- 处理大型文件:通过将文件分割为多个小块,您可以在客户端或服务器上逐步处理大型文件,而不是一次性加载整个文件。
- 实时数据处理:在处理实时数据流(如传感器数据、股票行情或实时聊天等)时,你可以使用
ReadableStream
在数据到达时立即处理数据,而不需要等待请求完成。 - 网络请求:Fetch API 返回的响应对象包含一个
ReadableStream
,它允许您逐步读取和处理从服务器返回的数据。这对于处理大型响应、视频流或音频流等非常有用。
更加具体的使用可以参照MDN
转载自:https://juejin.cn/post/7217244542080450617