reactjs - Kan inte använda `request.on`-händelsehanterare för att stänga strömförfrågan på NextJS


Jag skapar en API-endpoint som strömmar data så att jag kan uppdatera komponenter i realtid baserat på databashändelser. Jag lyckades nästan få allt att fungera, förutom städningen. Efter att jag skickat förfrågan ska endpointen strömma data kontinuerligt tills anslutningen stängs. Problemet är att den fortsätter köra strömningslogiken, och jag kunde inte använda request.on-händelser för att stoppa den.

Här är en mockande slutpunkt som jag har gjort för att visa det:

// pages/api/example.ts

import { NextApiRequest, NextApiResponse } from 'next'

const handler = async (req: NextApiRequest, res: NextApiResponse): Promise<void> => {
  if (req.method === 'GET') {
    console.log('requested')
    // headers
    res.setHeader('Access-Control-Allow-Origin', '*')
    res.setHeader('Content-Type', 'text/event-stream;charset=utf-8')
    res.setHeader('Cache-Control', 'no-cache, no-transform')
    res.setHeader('X-Accel-Buffering', 'no')

    setInterval(() => {
      console.log('ping')
      res.write('ping on res')
    }, 2000);

    let handleExit = () => {
      console.log('this will never run',)
      res.end()
    }

    req.on('close', () => {
      console.log('Close event triggered');
      handleExit();
    });
    req.on('finish', () => {
      console.log('Finish event triggered');
      handleExit();
    });
    req.on('end', () => {
      console.log('End event triggered');
      handleExit();
    });
    req.on('error', () => {
      console.log('Error event triggered');
      handleExit();
    });
    req.addListener('end', () => {
      console.log('End event triggered');
      handleExit();
    });
    req.on('readable', () => {
      console.log('Readable event triggered');
      handleExit();
    });
    console.log('all listeners set')
  }
}

export default handler

Nu, när jag curl -N localhost:3000/api/example kan jag se ping on res skrivas på min terminal varannan sekund, och NextJS-konsolens utdata ser ut som:

wait  - compiling /api/example (client and server)...
info  - Using external babel configuration from /home/node/src/.babelrc
event - compiled successfully in 1048 ms (96 modules)
requested
all listeners set
API resolved without sending a response for /api/example, this may result in stalled requests.
ping
ping
ping
ping
ping

...grejen är: även efter att jag stänger förfrågan (Ctrl+C), fortsätter den logga "ping". Som visas i outputen, kördes ingen av händelserna 'close', 'finish', 'end' osv.

Så, min fråga är: varför fungerar det inte och hur kan jag utföra en rengöring?

Svar #1

Den långvariga anslutningen skapas av ditt svar, inte av begäran. Därför kommer händelsen 'anslutning stängd' att utlösas på ditt svarobjekt.

Något sådant här borde fungera.

res.on("close", function () {
    console.log('End event triggered');
    handleExit();
});

Det betyder att din kod ska vara så här.

// store the interval variable so you can close interval later
const interval = setInterval(() => {
  console.log('ping')
  res.write('ping on res')
}, 2000);

// when your long-running respnose ends, clear the interval
res.on("close", function () {
    clearInterval(interval);
});

Observera. close-händelselyssnaren triggas endast efter att du försökt skriva till strömmen med res.write.