Agreed, this feels quite intuitive to me as well.
For rxjs & co I was trying to come up with a solution that could incorporate Suspense as well. Something like:
function signalFromStream(stream) {
const resolve, reject
const emissions = signal(new Promise((res, rej) => resolve = res; reject = rej))
const unsubscribe = stream.subscribe((value) => {
if (emissions.status = 'pending') resolve(value)
else emissions(Promise.resolve(value))
})
cleanup(unsubscribe)
return () => {
emissions()
}
}
but I'm not happy about constructing a promise and then constructing another promise for each subsequent emission. At least https://github.com/tc39/proposal-promise-with-resolvers will help with with the first issue. Apart from that, I would consider this an acceptable solution - it's not much different from the useState+useEffect dance that you would have to do to integrate with React.Hot/Cold doesn't really matter here. If it's cold, it will begin emitting as soon as we execute this function. If it's hot, upstream is responsible for deciding when to send & stop sending.
Side-note: I'm not actually trying to use rxjs in any of my current frontend projects. But the ubiquity of this observable pattern means that if you can integrate rxjs, you can probably integrate anything.