javascript - 优化用 TypeScript 编写的文件内容解析器类

标签 javascript typescript parallel-processing node-worker-threads

我有一个 typescript 模块(由 VSCode 扩展使用),它接受一个目录并解析文件中包含的内容。对于包含大量文件的目录,此解析需要一些时间,因此需要一些关于如何优化它的建议。
我不想复制/粘贴整个类文件,因此将使用包含我认为相关部分的模拟伪代码。

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 {
                // child is a folder
                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); // util method to synchronously read file content using fs

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

    async parseFileContent(content): Promise<any[]> {
        // parsing happens here and the file content is parsed into separate blocks
        let ast: any = await convertToAST(content); // uses an asynchronous method of an external dependency to convert content to AST
        let blocks = parseToBlocks(ast); // synchronous method called to convert AST to blocks

        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) {
                // this can take some time because if this condition is true, some external assets will be downloaded (via internet) 
                // on to the caller's machine + some additional processing takes place
                await processBlock(block);
            }
        }
        return blocks;
    }
}
仍然是 TypeScript/NodeJS 的初学者。如果可能的话,我正在这里寻找多线程/Java 风格的解决方案。在 Java 的上下文中,this.heavyFileProcessingMethod将是 Callable 的一个实例对象,该对象将被插入 List<Callable>然后将由 ExecutorService 并行执行返回 List<Future<Object>> .
基本上我希望所有文件都被并行处理,但是函数必须等待所有文件在从方法返回之前被处理(所以整个 iterate 方法只需要解析最大文件所花费的时间)。
一直在阅读 running tasks in worker threads in NodeJS ,这样的东西也可以在 TypeScript 中使用吗?如果可以,可以在这种情况下使用吗?如果我的 Parser需要重构类以适应此更改(或任何其他建议的更改),这没问题。
编辑:使用 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 {
            // child is a folder
            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;
    })
}

最佳答案

第一:Typescript 真的是 Javascript
Typescript 只是带有静态类型检查的 Javascript,这些静态类型在 TS 转译为 JS 时会被删除。由于您的问题是关于算法和运行时语言功能,Typescript 没有任何意义;你的问题是一个Javascript问题。所以马上告诉我们答案

Been reading on running tasks in worker threads in NodeJS, can something like this be used in TypeScript as well?


是是的。
至于你问题的第二部分,

can it be used in this situation?


答案是肯定的,但是...
第二:仅当任务受 CPU 限制时才使用工作线程。
可以并不一定意味着你应该。这取决于您的进程是受 IO 限制还是受 CPU 限制。如果它们是 IO 绑定(bind)的,那么依赖 Javascript 长期存在的异步编程模型(回调、Promises)很可能会好得多。但如果它们受 CPU 限制,那么使用 Node 对基于线程的并行性的相对较新的支持更有可能导致吞吐量增加。见 Node.js Multithreading! ,虽然我认为这个更好:Understanding Worker Threads in Node.js .
虽然工作线程比以前的 Node 并行选项(生成子进程)更轻,但与 Java 中的线程相比,它仍然相对较重。每个工作人员在自己的节点虚拟机中运行,常规变量不共享(您必须使用特殊的数据类型和/或消息传递来共享数据)。之所以必须这样做,是因为 Javascript 是围绕单线程编程模型设计的。它在该模型中非常高效,但这种设计使得对多线程的支持更加困难。这是一个很好的 SO 答案,为您提供有用的信息:https://stackoverflow.com/a/63225073/8910547
我的猜测是您的解析更多地受到 IO 限制,并且产生工作线程的开销将超过任何 yield 。但是试一试,这将是一次学习经历。 :)

关于javascript - 优化用 TypeScript 编写的文件内容解析器类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67576223/

相关文章:

angular - 自定义 ExceptionHandler 变化检测滞后

javascript - Typescript (3.8+),#private 字段的缺点是什么?

r - doRedis workers 在 windows 上立即关闭

c - OpenCL 内核似乎没有获取全局 id "globally"

php - 是否可以在不安装应用程序的情况下更新用户的 Facebook 状态?

javascript - 在jqgrid中实现删除和编辑操作

javascript - 从数组中删除重复值

javascript - Typescript - 更新箭头函数中的变量

concurrency - GPar 的数据并行性

javascript - 如何使用 Node 上传后删除文件?