阿川私房教材:學程式,拿 offer!

63 個專案實戰,直接上手!
無需補習,按步驟打造你的面試作品。

立即解鎖你的轉職秘笈

現代應用程式需要高效能和回應能力,要求開發人員掌握並行和並行性。 TypeScript 作為 JavaScript 的超集,提供了強大的工具和模式來管理這些複雜性。本指南從各個角度探討了這兩個概念,深入探討了利用 TypeScript 中的並發性和平行性的實際範例、模式和進階實踐。

並發與並行:主要區別

在進入程式碼之前,理解這些術語至關重要:

1.並發:

  • 定義:系統透過交錯執行(不一定同時執行)來處理多個任務的能力。

  • 範例:在事件循環中在處理資料庫查詢和處理文件上傳之間切換。

2.並行度:

  • 定義:利用多核心處理器同時執行多個任務。

  • 範例:同時在不同的核心上執行複雜的數學計算。

可視化:

想像一下一家餐廳:

  • 並發性:一位廚師在幾道菜之間執行多任務。

  • 並行性:多名廚師同時製作不同的菜餚。

TypeScript 中的並發

JavaScript 以及擴充的 TypeScript 在單執行緒事件循環上執行,這可能會使並發聽起來不可能。然而,並發性是透過非同步程式設計模型(如回調承諾非同步/等待)實現的。

1. 使用 Promise 實現並發

Promise 是在 TypeScript 中實現並發的最簡單方法之一。

const fetchData = (url: string) => {
  return new Promise<string>((resolve) => {
    setTimeout(() => resolve(`Data from ${url}`), 1000);
  });
};

const main = async () => {
  console.log('Fetching data concurrently...');
  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); // ["Data from https://api.example.com/1", "Data from https://api.example.com/2"]
};
main();

解釋:

  • Promise.all允許兩個獲取操作同時執行,從而節省時間。

2. 與 Async/Await 的並發

async/await簡化了 Promise 鏈,同時保持了非同步特性。

async function task1() {
  console.log("Task 1 started");
  await new Promise((resolve) => setTimeout(resolve, 2000));
  console.log("Task 1 completed");
}

async function task2() {
  console.log("Task 2 started");
  await new Promise((resolve) => setTimeout(resolve, 1000));
  console.log("Task 2 completed");
}

async function main() {
  console.log("Concurrent execution...");
  await Promise.all([task1(), task2()]);
  console.log("All tasks completed");
}
main();

TypeScript 中的平行性

雖然 JavaScript 本身不支援多線程,但 Web Workers 和 Node.js Worker Threads 支援並行性。這些功能利用單獨的執行緒來處理計算量大的任務。

1. Web Worker 的並行性

在瀏覽器環境中,Web Workers 在單獨的執行緒中執行腳本。

// worker.ts
addEventListener('message', (event) => {
  const result = event.data.map((num: number) => num * 2);
  postMessage(result);
});
// main.ts
const worker = new Worker('worker.js');

worker.onmessage = (event) => {
  console.log('Result from worker:', event.data);
};

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

2. Node.js 工作線程

對於伺服器端應用程式,Node.js 提供了worker_threads

// worker.js
const { parentPort } = require('worker_threads');
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('Worker result:', 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();
  }
}

// Usage
const queue = new TaskQueue(3);
for (let i = 0; i < 10; i++) {
  queue.enqueue(async () => {
    console.log(`Task ${i} started`);
    await new Promise((resolve) => setTimeout(resolve, 1000));
    console.log(`Task ${i} completed`);
  });
}

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:', result));
  });
} else {
  parentPort.on('message', (task) => {
    parentPort.postMessage(task * 2);
  });
}

挑戰與解決方案

1. 除錯非同步程式碼

  • 使用 Node.js 中的async_hooks等工具來追蹤非同步操作。

  • 使用支援除錯異步/等待程式碼的 IDE。

2. 錯誤處理

  • 將 Promise 包裝在try/catch區塊中或use .catch()Promise.all一起使用。

3. 競爭條件

避免共享狀態或使用鎖定機制。

並行和並行性的最佳實踐

1. 優先考慮非同步 I/O:避免阻塞主執行緒進行 I/O 密集型操作。

2. 使用工作執行緒執行 CPU 密集型任務:將繁重的運算卸載到工作執行緒或 Web Workers。

3.限制並發:使用任務佇列或像p-limit這樣的函式庫來控制並發等級。

4. 利用函式庫:使用Bull等函式庫進行任務佇列或使用Workerpool進行工作執行緒管理。

結論

並發性和平行性對於建立高效能、可擴展的 TypeScript 應用程式至關重要。並發性透過交錯任務來提高反應能力,而並行性則可以在多核心系統上同時執行。透過掌握這些概念,開發人員可以應對現代應用程式中的挑戰並提供無縫的使用者體驗。


我的個人網站:https://shafayet.zya.me


這就是如何關閉 Vim...😭😭😭

圖片說明


原文出處:https://dev.to/shafayeat/mastering-concurrency-and-parallelism-in-typescript-1bgf


共有 0 則留言


精選技術文章翻譯,幫助開發者持續吸收新知。

阿川私房教材:學程式,拿 offer!

63 個專案實戰,直接上手!
無需補習,按步驟打造你的面試作品。

立即解鎖你的轉職秘笈