Node.js blocking versus non-blocking



Resources
Blocking
import * as file_system from 'fs';
import * as path from 'path';

const Blocking = () => {
    // Blocks here until 'Data.txt' file is read:
    const data: Buffer = file_system.readFileSync(path.resolve(__dirname, '..') + "/Data.txt");
    console.log(data.toLocaleString()); // 'Data' is displayed...
    console.info("This text appears *AFTER* 'Data'");
}

export default Blocking;
Non-blocking
import * as file_system from 'fs';
import * as path from 'path';

const Non_blocking = () => {
    file_system.readFile((path.resolve(__dirname, '..') + "/Data.txt"), (error, data) => {
        if (error) throw error;
        console.log(data.toLocaleString()); // 'Data' is displayed...
    });
    console.info("This text appears *BEFORE* 'Data'");
}

export default Non_blocking;
Main program
import Blocking from './Blocking';
import Non_blocking from './Non_blocking';

(function Main() {
    console.clear();
    console.info("Executable file: " + __filename + "\n");
    Blocking();
    setTimeout((duration) => {
        console.info(duration + " elapsed...");
        Non_blocking();
    }, 2500, '2.5 sec.');
})();

Exercise

Events

Predefined event types identified by names like 'close', 'error', 'finish'… are available from (typed) resources like standard input, standard output….

(function Keyboard(information) {
    console.info(`${information}`);

    const output_stream = fs.createWriteStream(
        path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..') + "/Keyboard.txt");
    output_stream.on('error', (error) => console.info(`error: ${error.message}`));

    const input_stream = readline.createInterface({ // 'import readline from 'node:readline';'
        input: process.stdin,
        output: process.stdout,
        prompt: "Enter a sentence: "
    });
    input_stream.prompt();
    input_stream.on('line', (line) => {
        switch (line.trim()) {
            case 'exit':
                input_stream.close();
                break;
            default:
                output_stream.write(line + '\n');
                input_stream.prompt(); // Next sentence...
        }
    }).on('close', () => {
        output_stream.end();
        output_stream.on('finish', () => console.info("Sentences have been written to 'Keyboard.txt'..."));
        // Program stop (without error) is queued:
        setTimeout(() => process.exit(0));
    });
})("'Keyboard' app. started...");
Custom events

Node.js offers a native event model, which in particular allows custom event emission and reception. An advanced event model is based on the RxJS library.

Event reception

// import {channel} from "./Input"; // Compilation...
import {channel} from "./Input.js"; // Execution...

channel.on('html', (html) => console.info("***\n" + html));

Event emission

import events from "node:events";
import fs from "node:fs";
import path from "node:path";
import {fileURLToPath} from "node:url";

export const channel = new events.EventEmitter();

(function Input(information) {
    console.info(`${information}`);
    const __dirname = path.dirname(fileURLToPath(import.meta.url));
    const input_stream: fs.ReadStream = fs.createReadStream(path.resolve(__dirname, '..') + "/index.html", 'utf8');
    input_stream.on('error', (error) => console.info(`error: ${error.message}`));
    input_stream.on('data', (html) => channel.emit('html', html));
})("'Events' app. started...");
Illustration: extracting raw data from an audio file
export default class OpenSLR_org_88 { // https://gitlab.com/nicolasobin/att-hack
    …
    private static _Audio_file_processing(datum: any, audio_file_name: string): never | void {
        const audio_stream = file_system.createReadStream(OpenSLR_org_88.Path + audio_file_name);
        let audio = new Uint8Array;
        audio_stream.on('error', (error) => {
            throw new Error("Audio file extraction failed... " + error);
        });
        audio_stream.on('end', () => {
            datum.audio = audio;
            // File destruction?
            // file_system.unlink(OpenSLR_org_88.Path + audio_file_name, (error) => {
            //     if (error)
            //         console.error(OpenSLR_org_88.Path + audio_file_name + " audio file destruction failed... " + error);
            // });
        });
        audio_stream.on('data', (chunk) => {
            // console.assert(chunk instanceof Uint8Array && chunk.constructor.name === 'Buffer');
            // console.info(`Chunk of size ${chunk.length}... with raw data: ${chunk}`);
            // console.assert(audio.byteLength === audio.length);
            const _audio = new Uint8Array(audio.byteLength + chunk.length);
            _audio.set(audio); // Copy...
            _audio.set(chunk as Uint8Array, audio.byteLength); // Add...
            audio = _audio; // Update...
        });
    }
    …
}