编程开源技术交流,分享技术与知识

网站首页 > 开源技术 正文

node.js中使用http或express实现类似chatgpt的打字效果及踩坑。

wxchong 2024-07-16 10:24:41 开源技术 9 ℃ 0 评论

前言

如果服务端一次性返回数据,前端处理还是比较方便和简单的。

今天主要讨论分批返回的问题。


http模块 测试用例

1、新建一个html文件

<!DOCTYPE html>
<html lang="zh-CN">
  <head>
    <meta charset="UTF-8" />
    <title>打字效果</title>
  </head>
  <body>
    <h1>打字效果</h1>
    <div>
      <p id="output"></p>
    </div>
    <script>

      const output = document.getElementById('output');

fetch('http://localhost:3000/write')
  .then((response) => {
    const reader = response.body.getReader();
    const decoder = new TextDecoder('utf-8');

    return new ReadableStream({
      start(controller) {
        function push() {
          reader.read().then(({ done, value }) => {
            if (done) {
              controller.close();
              return;
            }
            const chunk = decoder.decode(value, { stream: true });
            output.innerHTML += chunk;
            setTimeout(push, 100);
          }).catch((error) => {
            console.log(error);
            controller.error(error);
          });
        }
        push();
      }
    });
  })
  .then((stream) => {
    const reader = stream.getReader();
    return new ReadableStream({
      start(controller) {
        function push() {
          reader.read().then(({ done, value }) => {
            if (done) {
              controller.close();
              return;
            }
            output.innerHTML += value;
            setTimeout(push, 100);
          }).catch((error) => {
            console.log(error);
            controller.error(error);
          });
        }
        push();
      }
    });
  })
  .then((stream) => {
    const reader = stream.getReader();
    reader.read().then(({ done }) => {
      if (done) {
        console.log('EOF');
      } else {
        console.log('Something went wrong');
      }
    }).catch((error) => {
      console.log(error);
    });
  })
  .catch((error) => {
    console.log(error);
  });
    </script>
  </body>
</html>

2、新建一个js文件

const http = require("http");
const fs = require("fs");

function index(req, res) {
  if (req.url === "/") {
    res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
    fs.readFile("generate/write-http.html", (err, data) => {
      res.end(err || data);
    });
  }
}

function favicon(req, res) {
  if (req.url === "/favicon.ico") {
    res.writeHead(404, { "Content-Type": "text/plain" });
    res.end("404 Not Found\n");
  }
}

function write(req, res) {
  res.writeHead(200, { "Content-Type": "text/plain; charset=utf-8" });

  const content =
    "欢迎来到 人工智能 的世界,我是一个使用 Node.js 实现的智能聊天机器人,可以回答你关于开发、科技、文化等多方面的问题。";
  let index = 0;
  let timer = setInterval(() => {
    if (index >= content.length) {
      clearInterval(timer);
      res.write("");
      res.end();
      return;
    }
    res.write(content[index] + (content[index + 1] || ""));

    index++;
    index++;
  }, 100);
}

http
  .createServer((req, res) => {
    if (req.url === "/") {
      return index(req, res);
    }
    if (req.url === "/favicon.ico") {
      return favicon(req, res);
    }
    write(req, res);
  })
  .listen(3000, () => {
    console.log("Server running at http://localhost:3000/");
  });

express

接入到某个express项目中,以下为不生效的代码示例,也就是变成了一次性返回,而不是多次。

const express = require("express");
const compression = require('compression');
const cors = require("cors");
const bodyParser = require("body-parser");

const app = new express();
app.use(compression());

// ... 其它代码 ...

app.get("/", (req, res) => {
  index(req, res);
});
app.get("/write", (req, res) => {
  write(req, res);
});
var server = app.listen(3000, () => {
  var host = server.address().address;
  var port = server.address().port;
  console.log("服务器启动成功了端口是", port);
});

运行代码后,发现打字效果消失了,并且反应也慢。那么您看出问题了吗?为什么会出现这种情况呢?

对的,就是compression导致的问题。compression中间件会自动压缩响应数据,但是压缩需要先收集完整的响应数据,然后再一次性压缩并发送到客户端。这就意味着,如果在使用compression中间件的情况下尝试多次发送响应数据,可能会导致部分数据丢失或客户端无法正常接收数据。

const express = require("express");
const compression = require('compression');
const cors = require("cors");
const bodyParser = require("body-parser");

const app = new express();
// app.use(compression());

注释代码后,整个效果就正常运转了。

如果需要在使用compression中间件的情况下多次发送响应数据,可以考虑使用HTTP流技术,例如使用Server-Sent Events(SSE)或WebSocket等。这些技术可以在建立长连接后,实现双向通信,并支持多次发送数据。


代码高亮

如果您在实现类似效果时,需要用到代码高亮,推荐使用highlight.js

人人为我,我为人人,欢迎您的浏览,我们一起加油吧。

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表