Enhancing the efficiency of a TypeScript-written file content parsing class

Seeking advice on optimizing a typescript module used by a VSCode extension. This module accepts a directory and parses the content within the files, which can be time-consuming for directories with a large number of files.

To avoid copying the entire class files, a mock pseudocode containing relevant parts will be used.

class Parser {
    constructor(_dir: string) {
        this.dir = _dir;
    }

    parse() {
        let tree: any = getFileTree(this.dir);

        try {
            let parsedObjects: MyDTO[] = await this.iterate(tree.children);
        } catch (err) {
            console.error(err);
        }
    }

    async iterate(children: any[]): Promise<MyDTO[]> {
        let objs: MyDTO[] = [];

        for (let i = 0; i < children.length; i++) {
            let child: any = children[i];

            if (child.type === Constants.FILE) {
                let dto: FileDTO = await this.heavyFileProcessingMethod(file); // this takes time
                objs.push(dto);
            } else {
                let dtos: MyDTO[] = await this.iterateChildItems(child.children);
                let dto: FolderDTO = new FolderDTO();
                dto.files = dtos.filter(item => item instanceof FileDTO);
                dto.folders = dtos.filter(item => item instanceof FolderDTO);
                objs.push(FolderDTO);
            }
        }

        return objs;
    }

    async heavyFileProcessingMethod(file: string): Promise<FileDTO> {
        let content: string = readFile(file);

        return new FileDTO(await this.parseFileContent(content));
    }

    async parseFileContent(content): Promise<any[]> {
        let ast: any = await convertToAST(content);
        let blocks = parseToBlocks(ast);

        return await this.processBlocks(blocks);
    }

    async processBlocks(blocks: any[]): Promise<any[]> {
        for (let i = 0; i < blocks.length; i++) {
            let block: Block = blocks[i];
            if (block.condition === true) {
                await processBlock(block);
            }
        }
        return blocks;
    }
}

For optimization purposes, looking for a multithreading/Java-esque solution in TypeScript/NodeJS. In Java context, this.heavyFileProcessingMethod would be an instance of Callable object pushed into a List<Callable> executed parallelly by an ExecutorService, returning List<Future<Object>>.

Plan is to process all files in parallel while waiting for all files to finish processing before returning from the method.

Considering using worker threads in NodeJS for this purpose. How can it be implemented in TypeScript? Will it work in this scenario?

If refactoring the Parser class is necessary to accommodate changes, that's not an issue.

EDIT: Implementing Promise.all

async iterate(children: any[]): Promise<MyDTO>[] {
    let promises: Promies<MyDTO>[] = [];

    for(let i = 0; i <children.length; i++) {
        let child: any = children[i];

        if (child.type === Constants.FILE) {
            let promise: Promise<FileDTO> = this.heavyFileProcessingMethod(file); // this takes time
            promises.push(promise);
        } else {
            let dtos: Promise<MyDTO>[] = this.iterateChildItems(child.children);
            let promise: Promise<FolderDTO> = this.getFolderPromise(dtos);
            promises.push(promise);
        }
    }

    return promises;
}

async getFolderPromise(promises: Promise<MyDTO>[]): Promise<FolderDTO> {
    return Promise.all(promises).then(dtos => {
        let dto: FolderDTO = new FolderDTO();
        dto.files = dtos.filter(item => item instanceof FileDTO);
        dto.folders = dtos.filter(item => item instanceof FolderDTO);
        return dto;
    })
}

Answer №1

Typescript vs Javascript: Understanding the Difference

While Typescript is often viewed as a separate language, it's essentially just Javascript with additional static type checking capabilities. These types are stripped away when the code is transpiled to plain Javascript. When discussing algorithms and runtime features, it's important to recognize that Typescript ultimately compiles down to Javascript, making it more of a tool for development rather than a distinct programming language.

If you've been exploring worker threads in NodeJS, can you leverage this functionality in Typescript?

The answer is YES.

However, when considering whether it's suitable for your specific situation,

Can it effectively be used in this scenario?

The response is YES, but...

Utilizing Worker Threads for CPU-Intensive Tasks

Although it's possible to implement worker threads in Typescript, it's essential to evaluate whether it aligns with your project requirements. The decision is influenced by whether your processes are primarily IO bound or CPU bound. For IO-bound operations, sticking with Javascript's asynchronous approach may yield better results through mechanisms like callbacks and Promises. On the other hand, if your tasks are CPU intensive, integrating Node's support for threaded parallelism could enhance performance. Explore resources such as Node.js Multithreading! or Understanding Worker Threads in Node.js for further insights.

Worker threads offer advantages over traditional methods like spawning child processes, yet they still come with certain considerations. Each worker operates within its own Node virtual machine, necessitating special data handling techniques for communication between threads. This complexity stems from Javascript's inherent single-threaded nature, which poses challenges for multithreaded implementations. Check out this informative SO answer for more details:

If your workload leans towards IO operations, the overhead of utilizing worker threads might outweigh the benefits. Nonetheless, experimenting with this feature can provide valuable insights and expand your skill set. :)

Answer №2

One major issue you seem to be facing is efficiently navigating through a complex nested directory structure while trying to manage promises associated with individual files and directories. To simplify this process, I recommend approaching it in a more streamlined manner.

You can create a function that takes a directory path as input and outputs a flat list containing all the files found within that directory, regardless of their depth, similar to how the find program functions. This function could look something like the following:

import * as fs from 'fs/promises'
import * as path from 'path'

async function fileList(dir: string): Promise<string[]> {
    let entries = await fs.readdir(dir, {withFileTypes: true})

    let files = entries
        .filter(e => e.isFile())
        .map(e => path.join(dir, e.name))

    let dirs = entries
        .filter(e => e.isDirectory())
        .map(e => path.join(dir, e.name))

    let subLists = await Promise.all(dirs.map(d => fileList(d)))

    return files.concat(subLists.flat())
}

In essence, this function retrieves the directory entries, identifies subdirectories, and recursively processes them concurrently. Once the recursive iteration is complete, it flattens, merges, and returns the resulting list.

By utilizing this function, you can easily apply your intensive task to all files simultaneously by leveraging map and Promise.all:

let allFiles = await fileList(dir)

let results = await Promise.all(allFiles.map(f => heavyTask(f)))

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Generate Pagination in JavaScript using an Array of Elements

Is there a way to implement a Pagination System using JavaScript? Specifically, I need to display 10 products per page. I currently have an Array containing various products and my goal is to iterate through these products to display the first 10 on one p ...

What could be causing my function to fail <object>?

Within index.php, I am calling the function twice, which includes chart.html. index.php chart_line($valuesNight); //first call chart_line($valuesEvening); //second call ?> <?php function chart_line($jsonDataSource){ ?> < ...

How to Add Functionality to Angular Apps Without Defining a Route

One unique aspect of my website is the navigation bar. It appears on some pages but not others, so I've created a controller specifically for it. Here's how the navigation bar markup looks: <html ng-app="myApp"> <head> <title& ...

I am looking to use flexbox and Vue.js to organize my sent messages in blue on the right and received messages in yellow on the left. How can I achieve this grouping effectively?

I've successfully built a messenger system using Vue.js with a Laravel backend. My goal is to display messages from myself (Jim, the logged in user) on the right side in blue and messages from Debbie (the recipient) on the left side in yellow, similar ...

Using the parameter value as a property name in the return type of a function in TypeScript: a guide

After creating a function that converts an object to an array where each element contains the ID of the object, I encountered a new requirement. The current function works great with the following code: const objectToArray = <T>(object: { [id: string ...

What is the best way to transform a JavaScript file retrieved from a remote server (returned as a string) into a standard JavaScript object in a Node

Is there a way to convert a JavaScript file fetched from a remote server (which is in string format) into a regular JavaScript object in Node.js? I know that I can use JSON.parse to convert a JSON string into a dictionary, but in this case, the file contai ...

Verify if a set of Radio buttons contains at least one option selected

Need help with validating a Feedback Form using either JQuery or Javascript. The requirement is to ensure that every group of radio-buttons has one option selected before the form can be submitted. Below is the code snippet from form.html: <form id=&ap ...

Here's a new version: "Strategies for deactivating a text field in a desktop application that

I am currently using WiniumDriver to automate a desktop application. My goal is to disable a text field once a value has been entered into it. // Launch the desktop application WiniumDriver driver = null; DesktopOptions option = new DesktopOptions(); o ...

Creating Dynamic Visibility in ASP.NET Server Controls: Displaying or hiding a textbox based on a dropdown selection

Is there a way to dynamically show/hide a textbox or an entire div section based on a user's selection from a dropdown menu? Can this be achieved using client-side HTML controls instead of server controls? Would utilizing jQuery provide the best solut ...

Can you explain the distinction between using angular.copy() and simply using an assignment (=) to assign values

When a button is clicked, I want to assign certain values using the event parameter. Here is the code: $scope.update = function(context) { $scope.master = context; }; The $scope.master has been assigned the values of user. Recently, I came across th ...

Can a form component be recycled through the use of inheritance?

Greetings to the Stackoverflow Community, As I delve into this question, I realize that examples on this topic are scarce. Before diving into specifics, let me outline my strategy. I currently have three pages with numerous FormGroups that overlap signif ...

Leveraging CustomPipe with ngModel in Angular 7 and beyond

In my application, I am facing an issue with a date calendar picker input that is storing dates on a server and returning them onInit. The problem arises when the date is not stored or picked, as it returns 01/01/0001 numbers. My goal is to have the input ...

Display loading spinner and lock the page while a request is being processed via AJAX

Currently, I am working on a project using MVC in C#, along with Bootstrap and FontAwesome. The main objective of my project is to display a spinner and disable the page while waiting for an ajax request. Up until now, I have been able to achieve this go ...

CSS Flexibility in Action

Presently, my tab bar has a fixed look as shown here: https://codepen.io/cdemez/pen/WNrQpWp Including properties like width: 400px; etc... Upon inspecting the code, you'll notice that all the dimensions are static :-( Consequently, I am encountering ...

What is the best way to halt a CSS transition using JavaScript when dealing with an image?

I am facing an issue while attempting to create a basic CSS transition using JavaScript. The goal is for the start button to trigger the movement of an element, based on the duration of the keyframe animation. Then, clicking the end button should make the ...

What is the best way to reset an a-select component in Ant Design Vue?

I need to programmatically reset the selection in my a-select component after a specific event is triggered. Here's an example of my a-select code snippet: <a-select style="marginTop: 8px;width: 20%" @change="onChanged" > ...

Executing a scroll down action with Selenium in combination with Node.js and the Chai/Mocha testing framework

Browser: Chrome Looking for ways to scroll up or down using Selenium with Node.js (JavaScript). ...

Issue with Mjpg paparazzo.js functionality not functioning as expected

I am currently exploring alternative methods to view my security cameras without relying on browser support for mjpg streaming. I have come across Paparazzo.js, which appears to be a promising solution that I want to experiment with: https://github.com/rod ...

Having trouble accessing dynamically generated elements using Selenium

I've been attempting to change the router's SSIDs using a Selenium script, but I'm encountering difficulty accessing any JS elements generated by the router page. I've tried various Expected Conditions and methods without success. Here ...

Add option button

Is there a way to dynamically add radio buttons using jQuery or JavaScript and save the data into a database? I have successfully appended input types such as text, textarea, checkbox, and select with options. Here is my code: <!DOCTYPE html> < ...