Streams API позволяет обрабатывать данные по частям, не дожидаясь полной загрузки ресурса. В контексте сервис-воркеров это особенно важно для реализации прогрессивной загрузки, оптимизации кэширования и модификации ответов на лету. Основные интерфейсы, используемые в сервис-воркере, это ReadableStream, WritableStream и TransformStream.
ReadableStream представляет поток данных, которые можно
читать по кускам. Он позволяет:
Пример создания ReadableStream внутри
сервис-воркера:
const stream = new ReadableStream({
start(controller) {
controller.enqueue(new TextEncoder().encode("Привет, "));
controller.enqueue(new TextEncoder().encode("мир!"));
controller.close();
}
});
Поток можно использовать для создания Response:
const response = new Response(stream, {
headers: { "Content-Type": "text/plain" }
});
Это позволяет отправлять данные пользователю по мере их генерации, не дожидаясь полной готовности ресурса.
WritableStream используется для записи данных по частям.
В сервис-воркере он чаще всего применяется для кэширования или
перенаправления данных в другой поток.
Пример записи в поток:
const writableStream = new WritableStream({
write(chunk) {
console.log("Записываем:", new TextDecoder().decode(chunk));
},
close() {
console.log("Поток закрыт");
}
});
const writer = writableStream.getWriter();
writer.write(new TextEncoder().encode("Часть 1"));
writer.write(new TextEncoder().encode("Часть 2"));
writer.close();
Такой подход обеспечивает контроль над процессом записи и позволяет реализовать прогрессивную обработку данных.
TransformStream объединяет чтение и запись, обеспечивая
возможность модификации данных на лету. В сервис-воркере это особенно
удобно для:
Пример использования TransformStream:
const upperCaseTransform = new TransformStream({
transform(chunk, controller) {
const text = new TextDecoder().decode(chunk);
controller.enqueue(new TextEncoder().encode(text.toUpperCase()));
}
});
fetch("/example.txt")
.then(response => response.body)
.then(stream => stream.pipeThrough(upperCaseTransform))
.then(transformedStream => new Response(transformedStream))
.then(resp => resp.text())
.then(console.log);
В этом примере весь текст из /example.txt преобразуется
в верхний регистр в процессе передачи, без полной загрузки в память.
Сервис-воркеры часто используют кэш для оптимизации работы веб-приложений. Streams API позволяет кэшировать данные по частям, что особенно важно для больших файлов.
Пример кэширования потока:
self.addEventListener("fetch", event => {
event.respondWith(
caches.open("dynamic-cache").then(async cache => {
const response = await fetch(event.request);
const { readable, writable } = new TransformStream();
response.body.pipeTo(writable); // поток направляется в TransformStream
cache.put(event.request, new Response(readable.clone()));
return new Response(readable);
})
);
});
Такой подход позволяет одновременно отправлять данные клиенту и сохранять их в кэш, экономя память и ускоряя обработку.
При работе с потоками важно корректно обрабатывать ошибки:
const stream = new ReadableStream({
start(controller) {
try {
controller.enqueue(new TextEncoder().encode("Данные"));
controller.close();
} catch (error) {
controller.error(error);
}
}
});
stream.cancel("Отмена по условию");
controller.error() — завершает поток с ошибкой.controller.close() — закрывает поток после завершения
передачи данных.stream.cancel() — позволяет отменить передачу, если
данные больше не нужны.Workbox облегчает создание стратегий кэширования, но встроенная поддержка потоков расширяет возможности:
import { registerRoute } from 'workbox-routing';
registerRoute(
({ request }) => request.destination === 'script',
async ({ request }) => {
const response = await fetch(request);
const upperCaseTransform = new TransformStream({
transform(chunk, controller) {
const text = new TextDecoder().decode(chunk);
controller.enqueue(new TextEncoder().encode(text.toUpperCase()));
}
});
return new Response(response.body.pipeThrough(upperCaseTransform), response);
}
);
TransformStream можно собирать статистику о
скорости загрузки.Streams API в сервис-воркерах открывает возможности для асинхронной,
оптимизированной работы с данными, интеграции с кэшем и динамической
модификации контента. Использование ReadableStream,
WritableStream и TransformStream позволяет
создавать сложные и эффективные стратегии доставки ресурсов, особенно в
сочетании с Workbox, обеспечивая высокую производительность
веб-приложений.