前言
如果服务端一次性返回数据,前端处理还是比较方便和简单的。
今天主要讨论分批返回的问题。
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。
人人为我,我为人人,欢迎您的浏览,我们一起加油吧。
本文暂时没有评论,来添加一个吧(●'◡'●)