fetch — Promise API; body stream читается один раз; отмена через AbortController. Главная задача — abort stale requests в search/route change.
Разбор
Body stream, read once. res.json(), res.text(), res.blob() consume body — второй вызов throws «body already read». Большие downloads — res.body как ReadableStream с progress.
AbortController → signal → abort(). Controller (remote) и signal (receiver) разделены. Abort → fetch rejects AbortError — отличайте от real errors.
Race condition. Search «re», «rea», «reac» — три request, ответы out of order: «re» после «reac» перезапишет UI. Abort previous → «last write wins» = «last requested», не «last arrived».
Fetch vs XHR:
| fetch | XMLHttpRequest |
|---|
| API | Promise, async/await | events / callbacks |
| Cancel | AbortController | xhr.abort() |
| Upload progress | ❌ | ✅ xhr.upload.onprogress |
| Timeout | manual (AbortSignal.timeout) | xhr.timeout |
| Streaming response | ✅ res.body | limited |
| Cookies cross-origin | credentials: 'include' | withCredentials |
XHR (или Axios) — когда нужен upload progress; у fetch его нет.
Итог
Master AbortController + res.ok; для upload progress — XHR или library wrapper.