← Wszystkie tematy W 12% ogłoszeń · 35 z 286 wszystkich ofert JS

Streaming i real-time

Zjeżdżalnia i telefon z puszek

Od tego ogłoszenia się zaczęło: emagine szuka React Engineera do czatu AI. Wymagają „real-time streaming UIs” i „complex client-side state”, a mile widziane są „streaming AI APIs (SSE-based LLMs)”. Chodzi o odpowiedź, która pojawia się słówko po słówku, jak w ChatGPT.

Opanowane 0 z 8 pytań

Co piszą w ogłoszeniach

Najpierw zagadka

Czemu słówko po słówku?

Dwa roboty opowiadają tę samą bajkę. Pierwszy czeka, aż wymyśli całą, i dopiero wtedy mówi. Drugi mówi każde słówko od razu, gdy je wymyśli. Który szybciej zacznie opowiadać?

Robot „wszystko naraz”

Jeszcze nic nie mówi.
Pierwsze słowo: –

Robot „po kawałku”

Jeszcze nic nie mówi.
Pierwsze słowo: –

Obaj kończą w tym samym momencie, ale drugi zaczyna mówić prawie od razu. Dorośli nazywają to TTFT, czyli czas do pierwszego tokenu. Dlatego czaty pokazują odpowiedź po kawałku.

Sposób pierwszy · SSE

Zjeżdżalnia

Rzucasz robotowi pytanie papierowym samolotem. Robot buduje do Ciebie zjeżdżalnię i puszcza nią słówka. Zjeżdżalnia działa tylko w jedną stronę: z góry na dół. Ty nic nią nie wyślesz.

czeka na pytanie
Tu pojawi się odpowiedź robota.
Sposób drugi · WebSocket

Telefon z puszek

Ty i robot trzymacie puszki połączone sznurkiem. Sznurek jest cały czas napięty, więc oboje możecie mówić, kiedy chcecie. Pytanie leci sznurkiem do robota, a odpowiedź wraca tym samym sznurkiem.

sznurek napięty
Tu pojawi się odpowiedź robota.
Porównajmy

Czym się różnią?

Zjeżdżalnia · SSEPuszki · WebSocket
Kto mówi? Tylko robot. Słówka jadą w dół.Strumień jednokierunkowy serwer → klient. Obaj, kiedy chcą.Pełny dupleks w jednym połączeniu.
Jak zadać pytanie? Samolotem, osobno. Na każde pytanie robot buduje nową zjeżdżalnię.Nowe żądanie HTTP POST z odpowiedzią text/event-stream. Przez ten sam sznurek, który już jest.Wiadomość JSON w otwartym sockecie, odpowiedzi rozróżniane po id.
Jak powiedzieć „stop”? Przecinasz zjeżdżalnię nożyczkami.AbortController zamyka całe żądanie. Wołasz „stop!” do puszki. Sznurek zostaje.Wiadomość {type: "stop", id}. Połączenie dalej żyje.
Co, jak się zepsuje? Pytasz jeszcze raz i robot buduje nową zjeżdżalnię.Nowe żądanie. EventSource umie też sam wznowić połączenie. Trzeba samemu zawiązać sznurek.Reconnect piszesz ręcznie: backoff, heartbeat, kolejka wiadomości.
Do czego najlepszy? Gdy robot dużo opowiada, a Ty słuchasz.Czaty AI (OpenAI, Anthropic), powiadomienia, kursy na żywo. Gdy wszyscy gadają naraz.Czat między ludźmi, gry online, wspólna edycja dokumentu.

Zjeżdżalnia

Robot mówi, Ty słuchasz. Proste i zwykle wystarcza do czatu z robotem.

Puszki

Obaj gadacie tym samym sznurkiem, kiedy chcecie. Więcej zabawy, ale sznurek trzeba pilnować.

Sprawdź się

Pytania z rozmów

Najpierw odpowiedz na głos, dopiero potem rozwiń odpowiedź.

  1. Czym różni się SSE od WebSocketa?

    Moja odpowiedź

    Zapisuje się samo w tej przeglądarce. Kopię zrobisz na stronie Notatki.

    Pokaż odpowiedź

    Dla przedszkolaka: Zjeżdżalnia działa w jedną stronę: robot puszcza słówka w dół. Telefon z puszek działa w obie strony: obaj mówicie tym samym sznurkiem.

    Na rozmowę: SSE to jednokierunkowy strumień serwer → klient po zwykłym HTTP (text/event-stream). WebSocket to pełny dupleks po HTTP Upgrade, z własnym protokołem ramek. SSE jest prostsze i lepiej działa z proxy, CDN i autoryzacją. WebSocket wybieram, gdy klient też nadaje na żywo.

    Umiem?
    Zapytaj Claude RozwińPrzepytaj mniePytania pogłębiające
  2. Dlaczego czaty AI zwykle używają SSE, a nie WebSocketa?

    Moja odpowiedź

    Zapisuje się samo w tej przeglądarce. Kopię zrobisz na stronie Notatki.

    Pokaż odpowiedź

    Dla przedszkolaka: Bo w czacie z robotem prawie cały czas mówi robot. Zjeżdżalnia w zupełności wystarczy.

    Na rozmowę: Ruch jest asymetryczny: jedno pytanie i długa odpowiedź. SSE to zwykły request HTTP, więc działa z load balancerem, nagłówkami Authorization, logowaniem i rate-limitingiem bez dodatkowej infrastruktury. Tak streamują OpenAI i Anthropic.

    Umiem?
    Zapytaj Claude RozwińPrzepytaj mniePytania pogłębiające
  3. Czemu do czatu nie użyjesz natywnego EventSource?

    Moja odpowiedź

    Zapisuje się samo w tej przeglądarce. Kopię zrobisz na stronie Notatki.

    Pokaż odpowiedź

    Na rozmowę: EventSource obsługuje tylko GET bez body i bez własnych nagłówków. Prompt i historię rozmowy trzeba wysłać POST-em, więc używam fetch + response.body.getReader() i sam parsuję eventy (rozdzielone pustą linią, linie event: i data:).

    Umiem?
    Zapytaj Claude RozwińPrzepytaj mniePytania pogłębiające
  4. Jak zaimplementujesz przycisk „Zatrzymaj generowanie”?

    Moja odpowiedź

    Zapisuje się samo w tej przeglądarce. Kopię zrobisz na stronie Notatki.

    Pokaż odpowiedź

    Dla przedszkolaka: Przy zjeżdżalni bierzesz nożyczki i ją przecinasz. Przy puszkach wołasz „stop!”, a sznurek zostaje.

    Na rozmowę: Przy SSE: AbortController.abort() na fetchu, a serwer na zdarzenie close przerywa generowanie. Przy WebSockecie wysyłam {type: "stop", id}, a połączenie zostaje otwarte. W obu przypadkach ignoruję fragmenty, które dotrą już po stopie, i oznaczam wiadomość jako stopped, nie error.

    Umiem?
    Zapytaj Claude RozwińPrzepytaj mniePytania pogłębiające
  5. Jak nie zamulić Reacta, gdy tokeny przychodzą co 20 ms?

    Moja odpowiedź

    Zapisuje się samo w tej przeglądarce. Kopię zrobisz na stronie Notatki.

    Pokaż odpowiedź

    Na rozmowę: Zbieram tokeny w buforze w useRef i robię setState najwyżej raz na klatkę (requestAnimationFrame). Wiadomości trzymam niemutowalnie i opakowuję w React.memo, więc przy każdym tokenie renderuje się tylko ta generowana. Przy bardzo długich rozmowach dochodzi wirtualizacja listy.

    Umiem?
    Zapytaj Claude RozwińPrzepytaj mniePytania pogłębiające
  6. Jak wyrenderować Markdown, który jeszcze się nie skończył?

    Moja odpowiedź

    Zapisuje się samo w tej przeglądarce. Kopię zrobisz na stronie Notatki.

    Pokaż odpowiedź

    Na rozmowę: Najczęstszy problem to otwarty blok ```. Na czas streamu domykam go tymczasowo, żeby reszta tekstu nie przeskakiwała między kodem a akapitem. Po zakończeniu renderuję oryginał. Parser markdowna dostaje cały tekst, a nie pojedynczy fragment.

    Umiem?
    Zapytaj Claude RozwińPrzepytaj mniePytania pogłębiające
  7. Jak wykryjesz zerwane połączenie i co zrobisz z niedokończoną odpowiedzią?

    Moja odpowiedź

    Zapisuje się samo w tej przeglądarce. Kopię zrobisz na stronie Notatki.

    Pokaż odpowiedź

    Dla przedszkolaka: Gdy sznurek pęknie, trzeba go zawiązać na nowo, a niedokończone zdanie pokazać jako „przerwane”.

    Na rozmowę: SSE: koniec strumienia bez eventu done to błąd. Do tego timeout bezczynności, bo serwer wysyła heartbeat. WebSocket: onclose odrzuca wszystkie trwające generacje, potem reconnect z exponential backoff i kolejką wiadomości. W UI zostawiam to, co już przyszło, i pokazuję błąd z przyciskiem „Ponów”.

    Umiem?
    Zapytaj Claude RozwińPrzepytaj mniePytania pogłębiające
  8. Jakie stany ma wiadomość w czacie i gdzie je trzymasz?

    Moja odpowiedź

    Zapisuje się samo w tej przeglądarce. Kopię zrobisz na stronie Notatki.

    Pokaż odpowiedź

    Na rozmowę: idle → waiting (wysłane, brak tokenów) → streaming → done / stopped / error. Trzymam to w reducerze: historia jako lista wiadomości, status na poziomie czatu i wiadomości. Transport ma prosty interfejs send(prompt, {onDelta, onDone, onError}) → stop, więc stan nie zależy od tego, czy pod spodem jest SSE, czy WS.

    Umiem?
    Zapytaj Claude RozwińPrzepytaj mniePytania pogłębiające
Poziom wyżej

Wersja dla seniora

Tak wyglądają pytania na seniora i leada: nie ma jednej dobrej odpowiedzi, liczy się to, jak ważysz kompromisy i czy mówisz o skali, ryzyku i ludziach.

Przypadek 1

Czat AI dla 50 tysięcy użytkowników, któremu zrywa się połączenie

Wasz asystent AI ma 50 tys. aktywnych użytkowników dziennie, wielu na mobilnym internecie. Odpowiedzi trwają do 60 sekund. Zgłoszenia mówią: „odpowiedź urywa się w połowie i trzeba pytać od nowa”, co kosztuje też tokeny modelu. Zaproponuj, jak sprawić, żeby zerwanie połączenia nie oznaczało utraty odpowiedzi.

Zanim rozwiniesz: powiedz na głos, jak byś to rozwiązał, i nazwij co najmniej jeden kompromis.

Moje rozwiązanie

Zapisuje się samo w tej przeglądarce. Kopię zrobisz na stronie Notatki.

Pokaż, czego szuka rekruter, i modelową odpowiedź

Co sprawdza rekruter

  • Czy oddzielasz generowanie odpowiedzi od połączenia, które ją dostarcza
  • Czy znasz mechanizm wznawiania SSE (id zdarzeń, Last-Event-ID)
  • Czy myślisz o koszcie, skali i stanie po stronie serwera
  • Czy wiesz, jak to pokazać w UI

Odpowiedź seniora

Kluczowa decyzja: generowanie nie może żyć w połączeniu HTTP. Serwer zapisuje fragmenty odpowiedzi do bufora (Redis Streams albo tabela z numerem fragmentu) pod id wiadomości, a połączenie SSE tylko je czyta. Każde zdarzenie ma id: <messageId>:<seq>.

Po zerwaniu klient łączy się ponownie z nagłówkiem Last-Event-ID (natywny EventSource robi to sam, przy fetch dodaję go ręcznie) i dostaje fragmenty od miejsca przerwania. Jeśli generowanie już się skończyło, dostaje resztę z bufora i zdarzenie done. Bufor ma TTL, np. 10 minut. Reconnect z exponential backoff i jitterem, żeby po awarii sieci 50 tys. klientów nie wróciło w tej samej sekundzie.

W UI zostawiam to, co już przyszło, pokazuję „wznawiam…” zamiast błędu, a dopiero po kilku nieudanych próbach przycisk „Ponów”. Metryki: odsetek przerwanych strumieni, liczba wznowień, czas do pierwszego tokenu i ile tokenów zaoszczędziliśmy dzięki wznawianiu.

Typowe wpadki

  • Odpowiedź „dodam retry, które wyśle pytanie jeszcze raz”
  • Trzymanie stanu generowania w pamięci jednego procesu Node bez sticky sessions
  • Reconnect bez jittera, który po awarii robi atak na własny serwer

Dopytają o

  • Co zrobisz, gdy użytkownik kliknie stop, a generowanie trwa na serwerze?
  • Jak to zadziała za load balancerem z wieloma instancjami?
  • Jak przetestujesz zrywanie połączenia w CI?
Zapytaj Claude Rozwiń caseZagraj interviewera
Przypadek 2

Ta sama rozmowa otwarta w trzech kartach i na telefonie

Użytkownicy otwierają czat w kilku kartach i na telefonie. Gdy w jednej karcie trwa generowanie, w drugiej rozmowa wygląda na pustą, a wysłanie pytania z dwóch miejsc naraz psuje historię. Produkt chce, żeby rozmowa była spójna wszędzie.

Zanim rozwiniesz: powiedz na głos, jak byś to rozwiązał, i nazwij co najmniej jeden kompromis.

Moje rozwiązanie

Zapisuje się samo w tej przeglądarce. Kopię zrobisz na stronie Notatki.

Pokaż, czego szuka rekruter, i modelową odpowiedź

Co sprawdza rekruter

  • Czy widzisz, że źródłem prawdy musi być serwer
  • Czy znasz BroadcastChannel / SharedWorker i ich ograniczenia
  • Czy umiesz obsłużyć współbieżne zapisy

Odpowiedź seniora

Źródłem prawdy jest serwer: rozmowa ma wersję, a każda nowa wiadomość jest dopisywana z oczekiwaną wersją (optimistic concurrency). Drugi zapis z tą samą wersją dostaje 409, a klient pokazuje „ta rozmowa jest właśnie aktywna na innym urządzeniu”.

Między kartami w jednej przeglądarce używam BroadcastChannel: karta, która generuje, rozgłasza fragmenty, a pozostałe tylko je wyświetlają. Można też trzymać jedno połączenie w SharedWorkerze, ale Safari na iOS go nie wspiera, więc to optymalizacja, a nie podstawa. Między urządzeniami subskrypcja zdarzeń rozmowy (SSE albo WebSocket per użytkownik), która po wznowieniu dociąga brakujące wiadomości po wersji.

Stan w kliencie trzymam w cache zapytań (TanStack Query) z kluczem rozmowy, a zdarzenia z innych kart robią setQueryData, więc UI ma jedno miejsce aktualizacji.

Typowe wpadki

  • Synchronizacja przez localStorage i zdarzenie storage jako główny mechanizm
  • Ignorowanie konfliktu „last write wins” bez informacji dla użytkownika
  • Otwieranie osobnego WebSocketu w każdej karcie bez limitu

Dopytają o

  • Ile połączeń na użytkownika jest dla was akceptowalne i dlaczego?
  • Co się stanie z historią, gdy telefon był offline przez godzinę?
  • Jak pokażesz, która karta „ma” generowanie?
Zapytaj Claude Rozwiń caseZagraj interviewera
Przypadek 3

Bardzo długie odpowiedzi zamulają stronę po kilku minutach

Odpowiedzi modelu bywają długie: kilka tysięcy linii kodu i tabel. Po kilku takich wiadomościach przewijanie zacina się, a profiler pokazuje, że każdy nowy token przelicza Markdown całej wiadomości i podświetlanie składni.

Zanim rozwiniesz: powiedz na głos, jak byś to rozwiązał, i nazwij co najmniej jeden kompromis.

Moje rozwiązanie

Zapisuje się samo w tej przeglądarce. Kopię zrobisz na stronie Notatki.

Pokaż, czego szuka rekruter, i modelową odpowiedź

Co sprawdza rekruter

  • Czy wiesz, gdzie jest koszt: parsowanie, podświetlanie, layout
  • Czy znasz techniki przyrostowego renderowania i wirtualizacji
  • Czy mierzysz zamiast zgadywać

Odpowiedź seniora

Najpierw pomiar w Performance panelu: zwykle koszt to pełne parsowanie Markdownu przy każdym flushu i podświetlanie wszystkich bloków kodu od nowa. Render ograniczam do raz na klatkę, ale to nie wystarcza przy 50 KB tekstu.

Dzielę wiadomość na bloki (akapity, bloki kodu). Ukończone bloki są zamrożone i memoizowane po treści, a parsuję tylko ostatni, rosnący blok. Podświetlanie składni uruchamiam dopiero po zamknięciu bloku kodu, w Web Workerze (Shiki lub Prism w workerze), a w trakcie pokazuję zwykły monospace.

Dla długiej historii wirtualizacja listy wiadomości (TanStack Virtual ze zmienną wysokością) albo content-visibility: auto na starszych wiadomościach. Budżet: przewijanie w 60 fps i INP poniżej 200 ms przy rozmowie z 50 wiadomościami, sprawdzany testem wydajności w CI.

Typowe wpadki

  • React.memo na całej wiadomości, która i tak zmienia się przy każdym tokenie
  • Podświetlanie składni w głównym wątku przy każdym fragmencie
  • Wirtualizacja bez obsługi zmiennej wysokości, przez co lista skacze

Dopytają o

  • Jak zachowasz pozycję przewijania, gdy użytkownik czyta wyżej?
  • Co z wyszukiwaniem tekstu (Ctrl+F) przy wirtualizacji?
  • Jak przetestujesz regres wydajności?
Zapytaj Claude Rozwiń caseZagraj interviewera

Skróty klawiszowe

/
szukaj tematu
Enter
w wyszukiwarce: otwórz pierwszy wynik
N
notatki do tematu
[ ]
poprzedni / następny temat
G potem T
lista tematów
G potem P
powtórka
G potem N
wszystkie notatki
D
jasny / ciemny motyw
?
ta ściąga
Spacja, 1, 2, 3
w powtórce: pokaż, oceń

Pod każdym pytaniem są też skróty do Claude: rozwinięcie tematu, próbna rozmowa i ocena Twojej odpowiedzi z notatki.