Split a ReadableStream into lines

2 min read 21-10-2024
Split a ReadableStream into lines


When working with streams in JavaScript, particularly ReadableStream, you may encounter a scenario where you need to split the stream into manageable chunks, such as lines of text. This task can be essential when processing text data, such as logs or user input.

Understanding the Problem

The task at hand is to take a ReadableStream and split it into separate lines for easier processing. Here’s an original code snippet that can be improved for clarity and functionality:

const stream = getReadableStreamSomehow();
let result = '';

stream.getReader().read().then(function processText({ done, value }) {
    if (done) {
        console.log('Stream finished.');
        return;
    }
    result += new TextDecoder().decode(value);
    stream.getReader().read().then(processText);
});

Corrected and Improved Code

Let’s enhance this code for better readability and functionality. The following example demonstrates how to split the stream into lines effectively:

const stream = getReadableStreamSomehow();
const decoder = new TextDecoder();
let result = '';

async function processStream() {
    const reader = stream.getReader();
    let { done, value } = await reader.read();

    while (!done) {
        result += decoder.decode(value, { stream: true });
        { done, value } = await reader.read();
    }

    // Split the result into lines
    const lines = result.split('\n');
    lines.forEach((line, index) => {
        console.log(`Line ${index + 1}: ${line}`);
    });

    console.log('Stream finished.');
}

processStream();

Analysis and Explanation

How It Works

  1. Reading the Stream: The getReader() method obtains a reader object, allowing us to read the stream's data. The read() method returns a promise that resolves to an object containing done and value.

  2. Decoding the Stream: Using TextDecoder, we decode the chunks of binary data into strings. The { stream: true } option allows the decoder to handle fragmented data correctly.

  3. Concatenation: The incoming data is concatenated into a single result string.

  4. Splitting into Lines: After finishing reading the stream, the entire string is split into lines using the split('\n') method, allowing us to handle each line separately.

  5. Output: Finally, we loop through each line and print it out for easy verification.

Practical Example

Suppose you're building an application that processes server logs delivered via a ReadableStream. Using the above technique, you can easily read, decode, and split log entries into lines for further analysis, such as filtering warnings or errors.

// Simulate reading server logs from a ReadableStream
const logStream = new ReadableStream({
    start(controller) {
        controller.enqueue(new TextEncoder().encode("INFO: Server started\n"));
        controller.enqueue(new TextEncoder().encode("WARNING: High memory usage\n"));
        controller.enqueue(new TextEncoder().encode("ERROR: Server crashed\n"));
        controller.close();
    }
});

// Implementing the line-splitting process
async function processLogStream(stream) {
    const decoder = new TextDecoder();
    let result = '';

    const reader = stream.getReader();
    let { done, value } = await reader.read();

    while (!done) {
        result += decoder.decode(value, { stream: true });
        ({ done, value } = await reader.read());
    }

    const lines = result.split('\n').filter(line => line); // Remove empty lines
    lines.forEach(line => {
        if (line.includes("ERROR")) {
            console.error(`Error found: ${line}`);
        } else {
            console.log(line);
        }
    });
}

processLogStream(logStream);

Additional Resources

Conclusion

Splitting a ReadableStream into lines can streamline data processing, making your application more efficient and easier to manage. By using the techniques outlined in this article, you can effectively read and manipulate stream data in JavaScript. For further learning, refer to the resources provided, and explore how to implement streams in your own applications.