为了账号安全,请及时绑定邮箱和手机立即绑定

TypeScript中的并发与并行编程:轻松搞定高效能应用

标签:
Typescript

现代应用不仅需要高性能,还需要高响应性,需要开发人员掌握并发性和并行性。作为 JavaScript 超集的 TypeScript 提供了强大的工具和模式来管理这些复杂性。本指南从各个角度探讨了这两个概念,深入探讨了实用示例、模式和高级实践,以利用 TypeScript 中的并发性和并行性。

并发性 vs 并行性:主要区别是什么?

在我们开始编码之前,理解这些术语非常重要:

1.并发:

  • 定义: 系统通过轮流执行多个任务(而不是同时进行,而是依次进行)的能力。
  • 示例: 在事件循环中,在处理数据库查询和文件上传任务之间交替进行。

2. 并列性:

  • 定义:利用多核处理器同时做多个任务。
  • 示例:比如,同时在不同的核心上进行复杂的数学运算。

可视化:
想象一下一家餐厅:

  • 并发: 一个厨师同时忙好几个菜。
  • 并行: 多个厨师同时做不同的菜。
TypeScript 中的并发编程

JavaScript,以及由此延伸的TypeScript,运行在一个单线程事件循环中,这可能会让人误以为并发是不可能实现的。然而,可以通过回调、Promise、async/await等异步编程模型来实现并发。

1. 使用Promise来实现并发
Promise是实现TypeScript中异步编程最简单的方式之一。

    const fetchData = (url: string) => {
      return new Promise<string>((resolve) => {
        setTimeout(() => resolve(`从${url}获取的数据`), 1000);
      });
    };

    const main = async () => {
      console.log('正在并发获取数据...');
      const data1 = fetchData('https://api.example.com/1');
      const data2 = fetchData('https://api.example.com/2');

      const results = await Promise.all([data1, data2]);
      console.log(results); // ["从https://api.example.com/1获取的数据", "从https://api.example.com/2获取的数据"]
    };
    main();

全屏模式 退出全屏

解释如下:

  • Promise.all 可以让两个 fetch 请求同时进行,节省时间成本。2. 用 async/await 实现并发 async/await 简化了 promise 链式调用的过程,同时保持异步特性不变。
    async function task1() {
      console.log("任务1开始了");
      await new Promise((resolve) => setTimeout(resolve, 2000));
      console.log("任务1完成了");
    }

    async function task2() {
      console.log("任务2开始了");
      await new Promise((resolve) => setTimeout(resolve, 1000));
      console.log("任务2完成了");
    }

    async function main() {
      console.log("并发执行开始...");
      console.log("等待所有任务完成:");
      await Promise.all([task1(), task2()]);
      console.log("所有任务都完成了");
    }
    main();

切换到全屏 切换退出全屏

TypeScript 中的并行编程

尽管 JavaScript 本身不支持多线程,但 Web Workers 和 Node.js 的 Worker Threads 可以实现并行计算。这些特性利用单独的线程来处理计算密集的任务。

1. Web Workers 用于并行计算
在浏览器环境中,Web Workers 可以在一个独立的线程中运行脚本。

// worker.ts
// 监听消息事件
addEventListener('message', (event) => {
  // 对接收到的数据进行处理,将每个数字乘以2
  const result = event.data.map((num: number) => num * 2);
  // 发送处理后的结果
  postMessage(result);
});

全屏进入。退出全屏。

    // main.ts
    const worker = new Worker('worker.js');

    worker.onmessage = (event) => {
      console.log('工人发来的结果:', event.data);
    };

    worker.postMessage([1, 2, 3, 4]);

切换到全屏模式 退出全屏

2. Node.js 工作线程
Node.js 提供了 worker_threads,用于处理多线程任务。

    // worker.js
    const { parentPort } = require('worker_threads');
    // 这是一个用于处理消息的worker.js示例
    parentPort.on('message', (data) => {
      const result = data.map((num) => num * 2);
      parentPort.postMessage(result);
    });

切换到全屏 切换回正常模式

    // main.js
    const { Worker } = require('worker_threads');

    const worker = new Worker('./worker.js');
    worker.on('message', (result) => {
      console.log('工作线程结果:', result);
    });
    worker.postMessage([1, 2, 3, 4]);

全屏 退出全屏

并行与并发编程的实用模式

1. 任务队列用于管理并发性
当处理大量任务时,任务队列有助于确保有序执行。

    class TaskQueue {
      private queue: (() => Promise<void>)[] = [];
      private running = 0;
      constructor(private concurrencyLimit: number) {}

      enqueue(task: () => Promise<void>) {
        this.queue.push(task);
        this.run();
      }

      private async run() {
        if (this.running >= this.concurrencyLimit || this.queue.length === 0) return;

        this.running++;
        const task = this.queue.shift();
        if (task) await task();
        this.running--;
        this.run();
      }
    }

    // 示例用法
    const queue = new TaskQueue(3);
    for (let i = 0; i < 10; i++) {
      queue.enqueue(async () => {
        console.log(`任务 ${i} 正在开始`);
        await new Promise((resolve) => setTimeout(resolve, 1000));
        console.log(`任务 ${i} 已完成`);
      });
    }

进入全屏,退出全屏

2. 利用工作池进行负载均衡
工作池能高效地将任务分配到多个工作者上。

import { Worker, isMainThread, parentPort, workerData } from 'worker_threads';

if (isMainThread) {
  const workers = Array.from({ length: 4 }, () => new Worker(__filename));
  const tasks = [10, 20, 30, 40];
  workers.forEach((worker, index) => {
    worker.postMessage(tasks[index]);
    worker.on('message', (result) => console.log('结果:', result));
  });
} else {
  parentPort.on('message', (task) => {
    parentPort.postMessage(task * 2);
  });
}

进入全屏 退出全屏

挑战及应对措施

1. 调试异步代码时遇到的问题

  • 使用像 async_hooks 这样的工具在 Node.js 中追踪(跟踪)异步操作。
  • 使用支持调试 async/await 代码的 IDEs。

2. 错误处理

  • try/catch 块包裹承诺,或者在 Promise.all 后面加 .catch()

3. 竞态条件
避免共享状态或使用锁机制来同步资源。

并发与并行的最佳实践

1. 优先使用异步 I/O: 避免在进行 I/O 密集型操作时阻塞主线程。
2. 使用 Worker 线程处理 CPU 密集型任务: 将计算密集型任务交给 worker 线程或 Web Workers。
3. 限制并发量: 使用任务队列或如 p-limit 之类的库来控制并发量。
4. 使用库: 使用 Bull 等任务队列库或 Workerpool 来管理 worker 线程。

结论部分
并发和并行对于构建高性能且可扩展的TypeScript应用至关重要。并发通过交错执行任务来提高响应性,而并行则可以在多核系统上同时执行任务。掌握这些概念,开发人员可以应对现代应用中的各种挑战,并提供流畅的用户体验。


你可以在这里找到我,我的个人主页是https://shafayet.zya.me


这是关闭Vim的方法哦...😭

如图所示,图片如下:
图片描述

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消