Readable Stream chunks streaming weird
Unanswered
White Ibis posted this in #help-forum
White IbisOP
In dev server, everything works perfectly, but when deployed to vercel, the end of chunks only get sent in the next chunk and some other weird things happen.
I have attached the dev console logs as well as those from when it's deployed on vercel. Is this a bug or am I doing something wrong? I am using the app router btw.
Context:
page.tsx:
useReadableStream.ts:
route.tsx:
I have attached the dev console logs as well as those from when it's deployed on vercel. Is this a bug or am I doing something wrong? I am using the app router btw.
Context:
page.tsx:
'use client'
import { useReadableStream } from '@/app/(testing)/stream-readable/useReadableStream'
import { Button } from '@/components/ui/button'
import { Progress } from '@/components/ui/progress'
/**
* @link https://vercel.com/docs/functions/streaming
* @link https://vercel.com/blog/an-introduction-to-streaming-on-the-web
*/
export default function StreamReadable() {
const { data, isLoading, fetcher } = useReadableStream('stream-readable/api')
return (
<div className='flex h-screen flex-col items-center justify-center'>
<Button
variant={'outline'}
type='submit'
disabled={isLoading}
loading={isLoading}
onClick={fetcher}
className='w-[60%] rounded-b-none border-b-0'
>
Listen to Readable Stream
</Button>
<Progress value={data.progress} className='h-1 w-[60%] rounded-none rounded-b-md' />
</div>
)
}useReadableStream.ts:
'use client'
import { StreamData } from '@/app/(testing)/stream-readable/types'
import { getURL } from '@/lib/utils'
import { useState } from 'react'
import { toast } from 'sonner'
export function useReadableStream(endpoint: string) {
const [isLoading, setIsLoading] = useState<boolean>(false)
const [data, setData] = useState<StreamData>({
complete: false,
success: false,
message: '',
progress: 0
})
async function fetcher() {
setIsLoading(true)
const decoder = new TextDecoder()
const response = await fetch(getURL() + endpoint)
const reader = response.body!.getReader()
while (true) {
const { value, done } = await reader.read()
if (done || !value) break
const string = decoder.decode(value)
console.log(string)
try {
const data = JSON.parse(string) as StreamData
setData(data)
} catch (e) {
console.log(e)
}
}
setIsLoading(false)
}
return { data, isLoading, fetcher }
}route.tsx:
import { delay } from '@/lib/utils'
export const runtime = 'nodejs'
export const dynamic = 'force-dynamic'
export async function GET() {
const encoder = new TextEncoder()
const readable = new ReadableStream({
async start(controller) {
controller.enqueue(
encoder.encode(
JSON.stringify({ complete: false, success: false, message: 'Hello', progress: 0 }) + '\n'
)
)
await delay(1500)
controller.enqueue(
encoder.encode(
JSON.stringify({ complete: false, success: false, message: 'Chunk 1', progress: 25 }) +
'\n'
)
)
await delay(1500)
controller.enqueue(
encoder.encode(
JSON.stringify({ complete: false, success: false, message: 'Chunk 2', progress: 50 }) +
'\n'
)
)
await delay(1500)
controller.enqueue(
encoder.encode(
JSON.stringify({ complete: false, success: false, message: 'Chunk 3', progress: 75 }) +
'\n'
)
)
await delay(1500)
controller.enqueue(
encoder.encode(
JSON.stringify({ complete: true, success: true, message: 'Bye', progress: 100 }) + '\n'
)
)
controller.close()
}
})
return new Response(readable, {
headers: { 'Content-Type': 'text/html; charset=utf-8' }
})
}3 Replies
White IbisOP
dev server v
everything works correctly
everything works correctly
deployed to vercel v
end of chunks only get sent in the next chunk, which then also misses the end and so on
end of chunks only get sent in the next chunk, which then also misses the end and so on
White IbisOP
Ok, if anyone comes across this later. This is just how new ReadableStream works. It decides chunk size based on network conditions.