WritableStream: примеры (JAVASCRIPT)
WritableStream(underlyingSink (object), strategy (object)): WritableStreamОсновы WritableStream
WritableStream представляет собой объект, позволяющий записывать потоковые данные в пункт назначения. Эта функция применяется для обработки больших объемов информации, которые нецелесообразно загружать в память целиком.
Конструктор принимает необязательный объект с тремя методами:
- start(controller) - инициализация, может быть асинхронной
- write(chunk, controller) - обработка каждого фрагмента данных
- close(controller) - завершение записи
- abort(reason) - прерывание с указанием причины
Объект возвращает экземпляр WritableStream со свойствами:
- locked - boolean, указывает на возможность получения writer
- getWriter() - метод для получения объекта WritableStreamDefaultWriter
- close() - завершение потока
- abort(reason) - прерывание потока
Базовые примеры
Простейший пример создания потока для записи строк:
const writableStream = new WritableStream({
write(chunk) {
console.log('Записано:', chunk);
},
close() {
console.log('Поток закрыт');
}
});
const writer = writableStream.getWriter();
await writer.write('Первая порция данных');
await writer.write('Вторая порция данных');
await writer.close();Записано: Первая порция данных Записано: Вторая порция данных Поток закрыт
Пример с асинхронной записью:
const stream = new WritableStream({
async write(chunk) {
await new Promise(resolve => setTimeout(resolve, 100));
console.log('Асинхронная запись:', chunk);
}
});
const writer = stream.getWriter();
writer.write('Данные 1').then(() => {
console.log('Запись завершена');
});Асинхронная запись: Данные 1 Запись завершена
Альтернативные подходы в JavaScript
Для записи данных существуют несколько альтернатив:
- Blob и FileWriter API - работа с файлами в браузере, требует поддержки File System Access API
- TransformStream - преобразование данных при записи, часто используется совместно с WritableStream
- Response.body - запись в HTTP-ответ, специализированное применение
- Node.js Streams - в среде Node.js доступны более традиционные потоковые API
WritableStream предпочтительнее для современных веб-приложений, работающих с большими данными, в то время как FileWriter подходит для операций с файловой системой.
Аналоги в других языках
Python предоставляет аналогичную функциональность:
# Python
async def write_stream():
class CustomWriter:
async def write(self, data):
print(f'Записано: {data}')
writer = CustomWriter()
await writer.write('пример данных')В PHP используются ресурсы потоков:
// PHP
$stream = fopen('php://output', 'w');
fwrite($stream, 'данные для записи');
fclose($stream);C предлагает низкоуровневые операции:
// C
FILE *stream = fopen('file.txt', 'w');
fwrite(data, sizeof(char), strlen(data), stream);
fclose(stream);Основное отличие JavaScript реализации - асинхронная природа и интеграция с современными веб-API.
Распространенные ошибки
Попытка получения writer при заблокированном потоке:
const stream = new WritableStream({
write(chunk) {}
});
const writer1 = stream.getWriter();
const writer2 = stream.getWriter(); // ОшибкаTypeError: Cannot get a writer on a locked WritableStream
Необработанные ошибки при записи:
const stream = new WritableStream({
write(chunk) {
throw new Error('Ошибка записи');
}
});
const writer = stream.getWriter();
writer.write('данные').catch(e => {
console.error('Перехвачено:', e.message);
});Перехвачено: Ошибка записи
Использование закрытого потока:
const writer = stream.getWriter();
await writer.close();
await writer.write('данные'); // ОшибкаTypeError: Cannot write to a closed WritableStream
История изменений
Спецификация WritableStream развивается вместе со Streams API:
- 2015 - первоначальная спецификация в рамках WhatWG
- 2017 - добавление поддержки TransformStream
- 2019 - интеграция с File System Access API
- 2021 - улучшения производительности и стабильности
- 2023 - расширенная поддержка TypeScript типов
Современные браузеры реализуют актуальную версию спецификации, но для старых сред могут потребоваться полифиллы.
Расширенные примеры
Запись в несколько потоков одновременно:
async function teeStream(data) {
const primaryStream = new WritableStream({
write(chunk) {
console.log('Основной поток:', chunk);
}
});
const secondaryStream = new WritableStream({
write(chunk) {
console.log('Дополнительный поток:', chunk);
}
});
const writer1 = primaryStream.getWriter();
const writer2 = secondaryStream.getWriter();
for (const item of data) {
await Promise.all([
writer1.write(item),
writer2.write(item.toUpperCase())
]);
}
await writer1.close();
await writer2.close();
}
teeStream(['a', 'b', 'c']);Основной поток: a Дополнительный поток: A Основной поток: b Дополнительный поток: B Основной поток: c Дополнительный поток: C
Контроль backpressure с помощью queueing strategy:
const highWaterMark = 2;
const stream = new WritableStream({
write(chunk) {
return new Promise(resolve => {
setTimeout(() => {
console.log('Обработано:', chunk);
resolve();
}, 500);
});
}
}, new CountQueuingStrategy({ highWaterMark }));
const writer = stream.getWriter();
for (let i = 1; i <= 5; i++) {
const ready = writer.ready;
console.log(`Запись ${i}, готовность: ${await ready}`);
writer.write(`Элемент ${i}`);
}Запись 1, готовность: undefined Запись 2, готовность: true Обработано: Элемент 1 Запись 3, готовность: true Обработано: Элемент 2 ...
Интеграция с File System Access API:
async function saveFile() {
const fileHandle = await window.showSaveFilePicker();
const writable = await fileHandle.createWritable();
const stream = new WritableStream({
write(chunk) {
return writable.write(chunk);
},
close() {
return writable.close();
},
abort() {
return writable.abort();
}
});
const writer = stream.getWriter();
await writer.write('Содержимое файла\n');
await writer.close();
}