Lưu lượng yêu cầu fetch và hiển thị động văn bản từ AI

Để thực hiện việc gửi yêu cầu lưu lượng và hiển thị văn bản từ AI một cách từng ký tự, bạn có thể làm theo các bước sau:

  1. Gửi yêu cầu lưu lượng: Sử dụng fetch để gọi đến API, trả về đối tượng Response chứa ReadableStream.
  2. Lấy bộ đọc lưu lượng: Sử dụng phương thức getReader() trên response.body để lấy một instance của ReadableStreamDefaultReader.
  3. Đọc dữ liệu từng phần: Sử dụng vòng lặp while hoặc for await để đọc dữ liệu từ bộ đọc, nhận được các khối dữ liệu dưới dạng Uint8Array.
  4. Giải mã và hiển thị: Sử dụng TextDecoder để chuyển đổi dữ liệu nhị phân thành chuỗi, sau đó cập nhật từng phần lên trang web mà không cần đợi toàn bộ dữ liệu hoàn thành.

Gửi yêu cầu lưu lượng

const response = await fetch('/api/chat', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ prompt: 'Xin chào, AI.' })
});
// response.body là một ReadableStream

Lấy và sử dụng bộ đọc

const reader = response.body.getReader();  // Khóa luồng, lấy bộ đọc
const decoder = new TextDecoder('utf-8'); // Giải mã từ Uint8Array sang chuỗi
let done = false;

while (!done) {
  const { value, done: streamDone } = await reader.read();
  done = streamDone;
  if (value) {
    const chunkText = decoder.decode(value, { stream: true });
    // Đã nhận được một đoạn chuỗi chunkText
    displayChunk(chunkText);
  }
}

Hiển thị dữ liệu khi đọc

<div id="output"></div>
<script>
  function displayChunk(text) {
    const output = document.getElementById('output');
    output.textContent += text;  // Hoặc sử dụng output.innerHTML += để thêm và định dạng lại
  }
</script>

Ví dụ với React

import React, { useState, useEffect } from 'react';

function StreamedText({ query }) {
  const [content, setContent] = useState('');

  useEffect(() => {
    let aborted = false;

    async function fetchData() {
      setContent('');
      const res = await fetch('/api/chat', { method: 'POST', body: JSON.stringify({ query }) });
      const reader = res.body.getReader();
      const decoder = new TextDecoder();
      let done = false;

      while (!done && !aborted) {
        const { value, done: streamDone } = await reader.read();
        done = streamDone;
        if (value) {
          const chunk = decoder.decode(value, { stream: true });
          // Thêm nội dung mới
          setContent(prev => prev + chunk);
        }
      }
    }

    fetchData();
    return () => { aborted = true; };
  }, [query]);

  return <div>{content}</div>;
}

export default StreamedText;

Lưu ý và tối ưu hóa

  • Xử lý lỗi: Bắt lỗi từ reader.read() hoặc fetch để hiển thị tùy chọn thử lại.
  • Tối ưu hiệu năng: Nếu lượng dữ liệu lớn, cân nhắc cập nhật giao diện mỗi khi tích lũy đủ độ dài nhất định để tránh quá nhiều lần render.
  • Tương thích: Safari hỗ trợ không đầy đủ cho API lưu lượng, nếu cần tương thích hãy sử dụng polyfill hoặc quay lại fetch().then(res => res.text()).
  • Dữ liệu lưu lượng JSON: Nếu backend trả về luồng đối tượng JSON được phân cách bằng dấu xuống dòng, sau khi giải mã, hãy cắt chuỗi theo \\n và xử lý từng đối tượng JSON riêng biệt.

Thẻ: fetch ReadableStream TextDecoder react streaming

Đăng vào ngày 2 tháng 6 lúc 19:54