- May 2025
-
www.theguardian.com www.theguardian.com
-
Der Artikel aus The Guardian vom 2. Januar 2021 beschreibt das Dogger Bank Windfarm-Projekt vor der Küste von North Yorkshire, das als größter Offshore-Windpark der Welt gilt. Das Projekt umfasst fast 200 Turbinen, die jeweils so hoch wie der Eiffelturm sind und genug Strom für 16.000 Haushalte erzeugen können. Der Windpark ist ein zentraler Bestandteil der britischen Pläne, bis 2050 klimaneutral zu werden und eine industrielle Revolution für das Zeitalter der kohlenstoffarmen Wirtschaft einzuleiten.
Das Projekt wird von SSE, einem der wenigen großen britischen Unternehmen für erneuerbare Energien, in drei Phasen während der 2020er Jahre gebaut. Es wird erwartet, dass es Tausende von Arbeitsplätzen schafft und die lokale Wirtschaft in den Küstenregionen belebt. Die britische Regierung setzt stark auf Offshore-Windenergie, um sowohl die Energieversorgung zu dekarbonisieren als auch das Wirtschaftswachstum durch grüne Arbeitsplätze zu fördern.
Der Artikel betont auch die Bedeutung von Innovationen und Investitionen in erneuerbare Energien, um die britische Industrie zu modernisieren und die Abhängigkeit von fossilen Brennstoffen zu verringern. [Zusammenfassung generiert mit Mistral] https://www.theguardian.com/business/2021/jan/02/dogger-banks-giant-turbines-herald-a-wind-of-change-in-uk-industry
-
- Dec 2024
-
news.ycombinator.com news.ycombinator.com
-
tail logs using SSE
这也是我能想到的使用场景
-
- Feb 2024
-
publications.vtt.fi publications.vtt.fi
-
3 See for instance the Goldfinger Factory, http://www.goldfingerfactory.com/ or Nea Guinea in Greece,www.neaguinea.org.4 The same core group of people in Greece established PRO.S.K.AL.O. (Cooperation Initiative for Social andSolidarity Economy), Bioscoop (a consumers’ food cooperative), Initiative 136 (a citizens’ initiative for themanagement of water resources, http://www.136.gr/article/citizens-bid-control-thessalonikis-water), as well as thePeople’s University of Social Solidarity Economy, http://www.univsse.gr/p/univsse-in-english.htmlPAGE 31
SSE orgs
-
- Nov 2023
-
www.theguardian.com www.theguardian.com
-
Krisensymptome in der internationalen Windenergiebranche sind zugleich Signale dafür, dass Länder wie die USA und Großbritannien ihre Selbstverpflichtungen zum Ausbau des Windsektors nicht einhalten werden. Während Siemens Gamesa Qualitätsprobleme hat, gefärden Inflation und Wirtschaftskrise die Finanzierung großer Offshore-Projekte. https://www.theguardian.com/environment/2023/oct/27/is-crisis-at-siemens-energy-symptom-of-a-wider-wind-power-problem
-
- Sep 2023
-
stackoverflow.com stackoverflow.com
-
``js // SSE only supports GET request export async function GET({ url }) { const stream = new ReadableStream({ start(controller) { // You can enqueue multiple data asynchronously here. const myData = ["abc", "def"] myData.forEach(data => { controller.enqueue(
data: ${data}\n\n`) }) controller.close() }, cancel() { // cancel your resources here } });return new Response(stream, { headers: { // Denotes the response as SSE 'Content-Type': 'text/event-stream', // Optional. Request the GET request not to be cached. 'Cache-Control': 'no-cache', } })
} ```
-
-
Tags
Annotators
URL
-
- Jul 2023
-
www.mnot.net www.mnot.net
Tags
Annotators
URL
-
- Jun 2023
-
-
```js /* * Response from cache / self.addEventListener('fetch', event => { const response = self.caches.open('example') .then(caches => caches.match(event.request)) .then(response => response || fetch(event.request));
event.respondWith(response); });
/* * Response to SSE by text / self.addEventListener('fetch', event => { const { headers } = event.request; const isSSERequest = headers.get('Accept') === 'text/event-stream';
if (!isSSERequest) { return; }
event.respondWith(new Response('Hello!')); });
/* * Response to SSE by stream / self.addEventListener('fetch', event => { const { headers } = event.request; const isSSERequest = headers.get('Accept') === 'text/event-stream';
if (!isSSERequest) { return; }
const responseText = 'Hello!'; const responseData = Uint8Array.from(responseText, x => x.charCodeAt(0)); const stream = new ReadableStream({ start: controller => controller.enqueue(responseData) }); const response = new Response(stream);
event.respondWith(response); });
/* * SSE chunk data / const sseChunkData = (data, event, retry, id) => Object.entries({ event, id, data, retry }) .filter(([, value]) => ![undefined, null].includes(value)) .map(([key, value]) =>
${key}: ${value}
) .join('\n') + '\n\n';/* * Success response to SSE from SW / self.addEventListener('fetch', event => { const { headers } = event.request; const isSSERequest = headers.get('Accept') === 'text/event-stream';
if (!isSSERequest) { return; }
const sseChunkData = (data, event, retry, id) => Object.entries({ event, id, data, retry }) .filter(([, value]) => ![undefined, null].includes(value)) .map(([key, value]) =>
${key}: ${value}
) .join('\n') + '\n\n';const sseHeaders = { 'content-type': 'text/event-stream', 'Transfer-Encoding': 'chunked', 'Connection': 'keep-alive', };
const responseText = sseChunkData('Hello!'); const responseData = Uint8Array.from(responseText, x => x.charCodeAt(0)); const stream = new ReadableStream({ start: controller => controller.enqueue(responseData) }); const response = new Response(stream, { headers: sseHeaders });
event.respondWith(response); });
/* * Result / self.addEventListener('fetch', event => { const { headers, url } = event.request; const isSSERequest = headers.get('Accept') === 'text/event-stream';
// Process only SSE connections if (!isSSERequest) { return; }
// Headers for SSE response const sseHeaders = { 'content-type': 'text/event-stream', 'Transfer-Encoding': 'chunked', 'Connection': 'keep-alive', }; // Function for formatting message to SSE response const sseChunkData = (data, event, retry, id) => Object.entries({ event, id, data, retry }) .filter(([, value]) => ![undefined, null].includes(value)) .map(([key, value]) =>
${key}: ${value}
) .join('\n') + '\n\n';// Map with server connections, where key - url, value - EventSource const serverConnections = {}; // For each request opens only one server connection and use it for next requests with the same url const getServerConnection = url => { if (!serverConnections[url]) { serverConnections[url] = new EventSource(url); }
return serverConnections[url];
}; // On message from server forward it to browser const onServerMessage = (controller, { data, type, retry, lastEventId }) => { const responseText = sseChunkData(data, type, retry, lastEventId); const responseData = Uint8Array.from(responseText, x => x.charCodeAt(0)); controller.enqueue(responseData); }; const stream = new ReadableStream({ start: controller => getServerConnection(url).onmessage = onServerMessage.bind(null, controller) }); const response = new Response(stream, { headers: sseHeaders });
event.respondWith(response); }); ```
-
-
Tags
Annotators
URL
-
-
-
```js self.addEventListener('fetch', event => { const { headers, url } = event.request; const isSSERequest = headers.get('Accept') === 'text/event-stream';
// We process only SSE connections if (!isSSERequest) { return; }
// Response Headers for SSE const sseHeaders = { 'content-type': 'text/event-stream', 'Transfer-Encoding': 'chunked', 'Connection': 'keep-alive', }; // Function formatting data for SSE const sseChunkData = (data, event, retry, id) => Object.entries({ event, id, data, retry }) .filter(([, value]) => ![undefined, null].includes(value)) .map(([key, value]) =>
${key}: ${value}
) .join('\n') + '\n\n'; // Table with server connections, where key is url, value is EventSource const serverConnections = {}; // For each url, we open only one connection to the server and use it for subsequent requests const getServerConnection = url => { if (!serverConnections[url]) serverConnections[url] = new EventSource(url);return serverConnections[url];
}; // When we receive a message from the server, we forward it to the browser const onServerMessage = (controller, { data, type, retry, lastEventId }) => { const responseText = sseChunkData(data, type, retry, lastEventId); const responseData = Uint8Array.from(responseText, x => x.charCodeAt(0)); controller.enqueue(responseData); }; const stream = new ReadableStream({ start: controller => getServerConnection(url).onmessage = onServerMessage.bind(null, controller) }); const response = new Response(stream, { headers: sseHeaders });
event.respondWith(response); }); ```
-
- Mar 2023
-
Tags
Annotators
URL
-
-
developer.mozilla.org developer.mozilla.org
-
-
```js // Set up some pub/sub on the server
import { EventEmitter } from "events"; export let emitter = new EventEmitter();
// Set up an event stream with cleanup and queues // and stuff that subscribes to it and streams the // events when new stuff comes through:
import { emitter } from "../some-emitter.server";
type InitFunction = (send: SendFunction) => CleanupFunction; type SendFunction = (event: string, data: string) => void; type CleanupFunction = () => void;
export function eventStream(request: Request, init: InitFunction) { let stream = new ReadableStream({ start(controller) { let encoder = new TextEncoder(); let send = (event: string, data: string) => { controller.enqueue(encoder.encode(
event: ${event}\n
)); controller.enqueue(encoder.encode(data: ${data}\n\n
)); }; let cleanup = init(send);let closed = false; let close = () => { if (closed) return; cleanup(); closed = true; request.signal.removeEventListener("abort", close); controller.close(); }; request.signal.addEventListener("abort", close); if (request.signal.aborted) { close(); return; } },
});
return new Response(stream, { headers: { "Content-Type": "text/event-stream" }, }); }
// Return the event stream from a loader in // a resource route:
import { eventStream } from "./event-stream";
export let loader: LoaderFunction = ({ request }) => { return eventStream(request, send => { emitter.addListener("messageReceived", handleChatMessage);
function handleChatMessage(chatMessage: string) { send("message", chatMessage); } return () => { emitter.removeListener("messageReceived", handleChatMessage); };
}); };
// Push into the event emitter in actions:
import { emitter } from "./some-emitter.server";
export let action: ActionFunction = async ({ request }) => { let formData = await request.formData(); emitter.emit("messageReceived", formData.get("something"); return { ok: true }; };
// And finally, set up an EventSource in the browser
function useEventSource(href: string) { let [data, setData] = useState("");
useEffect(() => { let eventSource = new EventSource(href); eventSource.addEventListener("message", handler);
function handler(event: MessageEvent) { setData(event.data || "unknown"); } return () => { eventSource.removeEventListener("message", handler); };
}, []);
return data; } ```
Tags
Annotators
URL
-
-
github.com github.com
-
www.jacobparis.com www.jacobparis.com
-
We present deferred data by using React Suspense to conditionally show the content when it's ready. Suspense provides a fallback element to show when the data is not yet ready. Normally a loading spinner would go here, but we can use that to show our streamed progress instead.
js export default function Index() { const data = useLoaderData() const params = useParams() const stream = useEventSource( `/items/${params.hash}/progress`, { event: "progress", }, ) return ( <div> <Suspense fallback={<span> {stream}% </span>}> <Await resolve={data.promise} errorElement={<p>Error loading img!</p>} > {(promise) => <img alt="" src={promise.img} />} </Await> </Suspense> </div> ) }
-
On the client, while we're waiting for our deferred promise to resolve, we can consume that stream to know how far along our process is.
js const stream = useEventSource( `/items/${params.hash}/progress`, { event: "progress", }, )
-
In Remix, we can use a resource route to make this endpoint, and our loader will return a stream that constant checks our JSON file for its progress.
js export async function loader({ request, params, }: LoaderArgs) { const hash = params.hash return eventStream(request.signal, function setup(send) { const interval = setInterval(() => { const file = fs.readFileSync( path.join("public", "items", `${hash}.json`), ) if (file.toString()) { const data = JSON.parse(file.toString()) const progress = data.progress send({ event: "progress", data: String(progress) }) if (progress === 100) { clearInterval(interval) } } }, 200) return function clear(timer: number) { clearInterval(interval) clearInterval(timer) } }) }
-
server sent events work by having an endpoint that does not immediately close its connection, and which sends a content type of text/event-stream.
-
-
deno.com deno.com
Tags
Annotators
URL
-
- Jan 2023
-
datatracker.ietf.org datatracker.ietf.org
- Dec 2022
-
mercure.rocks mercure.rocks
-
-
html.spec.whatwg.org html.spec.whatwg.org
-
h3poteto.github.io h3poteto.github.io
Tags
Annotators
URL
-
-
docs.joinmastodon.org docs.joinmastodon.org
Tags
Annotators
URL
-
-
developer.mozilla.org developer.mozilla.org
Tags
Annotators
URL
-
- Oct 2018
-
www.npmjs.com www.npmjs.com
Tags
Annotators
URL
-