Edit

close()

Environment: client & server.

Manually close Telefunc streams.

The close() function is one of several ways to manually close streams — see all methods at How to close.

You usually don't need to manually close streams yourself — see: When to close.

For listening to streams closing, see: API > onClose().

When to close

A stream automatically closes itself when the client stops using it.

How does it work? When a stream object becomes unreachable (the client drops all references to it), the browser's garbage collector clears the stream object and the stream automatically closes itself.

You therefore usually don't have to manually close streams yourself.

That said, there is a short delay (typically a few seconds) between when the client stops using the stream and when the stream is closed. (The garbage collector doesn't always immediately clear unused objects.)

For example:

// Chat.jsx
// Environment: client
 
import { useEffect, useState } from 'react'
import { onChat } from './Chat.telefunc'
 
function Chat() {
  const [messages, setMessages] = useState([])
 
  useEffect(() => {
    const open = onChat() // Promise<{ channel }>
    open.then(({ channel }) => {
      channel.listen((msg) => setMessages((prev) => [...prev, msg]))
    })
    // Optional:
    const clear = () => open.then(({ channel }) => channel.close())
    return clear
  }, [])
 
  return (
    <ul>
      {messages.map((m, i) => (
        <li key={i}>{m}</li>
      ))}
    </ul>
  )
}
// Chat.tsx
// Environment: client
 
import { useEffect, useState } from 'react'
import { onChat } from './Chat.telefunc'
 
function Chat() {
  const [messages, setMessages] = useState<string[]>([])
 
  useEffect(() => {
    const open = onChat() // Promise<{ channel }>
    open.then(({ channel }) => {
      channel.listen((msg) => setMessages((prev) => [...prev, msg]))
    })
    // Optional:
    const clear = () => open.then(({ channel }) => channel.close())
    return clear
  }, [])
 
  return <ul>{messages.map((m, i) => <li key={i}>{m}</li>)}</ul>
}
// Clock.jsx
// Environment: client
 
import { useEffect, useState } from 'react'
import { onClock } from './Clock.telefunc'
 
function Clock() {
  const [n, setN] = useState(0)
 
  useEffect(() => {
    const clock = onClock() // long-lived AsyncGenerator
    ;(async () => {
      for await (const tick of clock) setN(tick)
    })()
    // Optional:
    const clear = () => void clock.return() // a running loop won't stop on its own
    return clear
  }, [])
 
  return <p>{n}</p>
}
// Clock.tsx
// Environment: client
 
import { useEffect, useState } from 'react'
import { onClock } from './Clock.telefunc'
 
function Clock() {
  const [n, setN] = useState(0)
 
  useEffect(() => {
    const clock = onClock() // long-lived AsyncGenerator
    ;(async () => {
      for await (const tick of clock) setN(tick)
    })()
    // Optional:
    const clear = () => void clock.return() // a running loop won't stop on its own
    return clear
  }, [])
 
  return <p>{n}</p>
}

Returning clear is optional: if you don't, the stream still closes itself automatically after React unmounts the component — the garbage collector clears the stream object and then the stream closes itself.

That said, if you want the stream to close as soon as possible, manually close it yourself — return clear in this example.

How to close

From the client, you can manually close a stream early with close() — or simply stop reading: break out of a for await, reader.cancel() a ReadableStream, or channel.close() a channel.

There's one catch-all API, close(), plus several per-stream-primitive APIs.

APISideGraceful / immediateCloses
close(value)clientGracefulEverything in the returned value (walked recursively)
gen.return() / breakclientGracefulOne AsyncGenerator
reader.cancel()clientGracefulOne ReadableStream
channel.close()client & serverGracefulOne Channel / BroadcastChannel (both ends)
channel.abort(value?)client & serverImmediateOne Channel / BroadcastChannel, with an abort value
abort(call)clientImmediateThe in-flight call and any channels it opened
withContext(fn, { signal })clientImmediateThe call and any channels it opened, via an AbortSignal

Graceful: flush buffered data, then close.
Immediate: cancel in-flight work.

close()

Gracefully closes all streams: in one call, it closes every stream the telefunction call opened.

// Environment: client
 
import { close } from 'telefunc/client'
import { onLoadDashboard } from './Dashboard.telefunc'
 
const result = await onLoadDashboard()
 
// Read what you need...
const { value } = await result.stream.next()
 
// Then close everything at once
close(result)

abort()

To stop a call immediately instead of gracefully, use abort() — the request is cancelled, and the pending call rejects with an Abort error (or, if you're mid-stream, that error surfaces on the next read instead):

// Environment: client
 
import { abort } from 'telefunc/client'
 
const call = onLoadDashboard()
abort(call) // cancels the in-flight call

To abort via an AbortSignal instead, use withContext(fn, { signal }).

See also