grommett/fastify-chunk-view

Stream is only sent after it has ended?

Closed this issue · 12 comments

jcbbb commented

I am trying to test your package, but it seems to be sending html after all promises have been resolved? Is it an issue with fastify, because I used to this kind of stuff with express. I also see you have asked same question here fastify/help#417. Have you been able to solve this issue?

Thanks

@jcbbb I just tested on my side and things seem to be working as expected. Do you have a sample of the code that's breaking?

Is it an issue with Fastify?

No Fastify works as expected.

I also see you have asked same question here fastify/help#417. Have you been able to solve this issue?

Yes, I was able to solve the issue. The problem was that I was sending the response stream only after I had pushed data into the stream, which is not the right order. The correct way is first send the stream then start pushing data into it.

Have a look at the code where I'm doing that:
https://github.com/grommett/fastify-chunk-view/blob/main/index.js#L21

Are you putting a Promise in the chunk array? If so, that might be your problem.

From the docs:

The fastify-chunk-view plugin takes an array of chunks and sends them to the client in that order. A chunk can be:
A string
A function that returns a string
An async function that returns a string
A Readable stream

jcbbb commented

I was putting a function that returns a promise, just like the productList example you have provided. I am not by my laptop right now, but I can double-check when I get home. Is the example in the readme working for you?

Hi @jcbbb the example in the Readme is working for me 🤷‍♂️ . I am only guessing at this point, but maybe you are calling the function instead of just passing the reference when adding it to the array?

// This won't work
reply.chunkView([asyncFunction()]

// This will
reply.chunkView([asyncFunction]

Also, when you get back to you computer one more question. In the final HTML output do you see
<!-- No strategy found for chunk -->

jcbbb commented
const products = [
  {
    title: "Macbook pro",
  },
  {
    title: "Asus zenbook",
  },
  {
    title: "Asus ROG",
  },
];

const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

async function getProducts() {
  await wait(2000);
  return `<ul>${products.map((product) => `<li>${product.title}</li>`).join("")}</ul>`;
}

const app = fastify({ logger: true });

app.get('/', (req, res) => {
  res.chunkView(['<p>This should render immediately.</p>', getProducts, '<p>This is should render after getProducts completed.</p>'])
})

Hey @grommett, this is the code I am using, I am getting response only after 2 seconds. Have you tried delaying your getProducts example?

jcbbb commented
  const app = express();
  
  app.get("/", async (req, res) => {
    res.write("<p>This should render first</p>");
    const products = await getProducts();
    res.write(products);
    res.write("<p>This should render after products</p>");
    res.end();
  }) 

This, on the other hand, works as expected.

jcbbb commented

And your latest commit shows my exact problem 😄. It's only rendering after 1 second delay that you have added.

@jcbbb Odd. Did you run the new example in the examples folder?

I can't reproduce on my side. What version of node are you using? Running the code sample you provided, with imports etc with Node v16 in Chrome, I see:

  1. This should render immediately
  2. Two seconds pass...
  3. The product list is rendered to the browser

Here is the code I'm running on my side:

const fastify = require('fastify');
const fastifyChunkView = require('fastify-chunk-view');

const products = [
  {
    title: 'Macbook pro',
  },
  {
    title: 'Asus zenbook',
  },
  {
    title: 'Asus ROG',
  },
];

const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

async function getProducts() {
  await wait(2000);
  return `<ul>${products.map((product) => `<li>${product.title}</li>`).join('')}</ul>`;
}

const app = fastify({ logger: true });
app.register(fastifyChunkView, {});
app.get('/', (req, res) => {
  res.chunkView([
    '<p>This should render immediately.</p>',
    getProducts,
    '<p>This is should render after getProducts completed.</p>',
  ]);
});

app.listen(3000, (err) => {
  if (err) throw err;
  // eslint-disable-next-line no-console
  console.log(`server listening on ${app.server.address().port}`);
});
jcbbb commented

Oh shit, I found the problem. I was using browser-sync for livereloading browser, and I guess it does not work with streaming. When I started server without it, it started working! 😄

jcbbb commented

Sorry to disturb you, both your package and fastify was fine)

Glad to hear it works for you