Предпосылка
Я пытаюсь найти правильный способ преждевременного прекращения серии потоковых потоков (конвейер) в Node.js: иногда я хочу изящно прервать поток до его завершения. В частности, я имею дело с главным образом objectMode: true
и не-родными параллельными потоками, но это не имеет большого значения.
Проблема
Проблема заключается в том, когда я unpipe
конвейер, данные остаются в каждом буфере потока и drain
ed. Это может быть хорошо для большинства промежуточных потоков (например, Readable
/Transform
), но последний Writable
по-прежнему истощается до его цели записи (например, файла или базы данных или сокета или w/e). Это может быть проблематично, если буфер содержит сотни или тысячи кусков, которые требуют значительного количества времени для слива. Я хочу, чтобы он немедленно остановился, то есть не слил; зачем тратить циклы и память на данные, которые не имеют значения?
В зависимости от маршрута я получаю либо ошибку "после записи", либо исключение, когда поток не может найти существующие каналы.
Вопрос
Каков правильный способ изящного уничтожения конвейера потоков в форме a.pipe(b).pipe(c).pipe(z)
?
Решение?
Решение, которое я придумал, состоит из трех шагов:
-
unpipe
каждый поток в конвейере в обратном порядке - Пустой буфер потока, который реализует
Writable
-
end
каждый поток, реализующийWritable
Некоторые псевдокоды, иллюстрирующие весь процесс:
var pipeline = [ // define the pipeline
readStream,
transformStream0,
transformStream1,
writeStream
];
// build and start the pipeline
var tmpBuildStream;
pipeline.forEach(function(stream) {
if ( !tmpBuildStream ) {
tmpBuildStream = stream;
continue;
}
tmpBuildStream = lastStream.pipe(stream);
});
// sleep, timeout, event, etc...
// tear down the pipeline
var tmpTearStream;
pipeline.slice(0).reverse().forEach(function(stream) {
if ( !tmpTearStream ) {
tmpTearStream = stream;
continue;
}
tmpTearStream = stream.unpipe(tmpTearStream);
});
// empty and end the pipeline
pipeline.forEach(function(stream) {
if ( typeof stream._writableState === 'object' ) { // empty
stream._writableState.length -= stream._writableState.buffer.length;
stream._writableState.buffer = [];
}
if ( typeof stream.end === 'function' ) { // kill
stream.end();
}
});
Меня действительно беспокоит использование stream._writableState
и изменение внутренних свойств buffer
и length
(_
означает частное свойство). Это похоже на хак. Также обратите внимание на то, что, поскольку я пишу, такие вещи, как pause
и resume
, наш вопрос не может быть (на основании предложения, полученного мной от IRC).
Я также собрал исполняемую версию (довольно неряшливую), которую вы можете захватить из github: https://github.com/zamnuts/multipipe-proto (git clone, npm install, просмотр readme, запуск npm)