Làm chủ tính đồng thời và tính song song trong TypeScript

0 0 0

Người đăng: Thái Thịnh

Theo Viblo Asia

Các ứng dụng hiện đại đòi hỏi hiệu suất cao và khả năng phản hồi nhanh, yêu cầu các nhà phát triển phải nắm vững xử lý đồng thời và song song. TypeScript, với tư cách là một siêu tập của JavaScript, cung cấp các công cụ và mẫu mạnh mẽ để quản lý những sự phức tạp này.

Xử lý Đồng thời vs. Song song: Sự khác biệt chính

Trước khi đi vào code, điều quan trọng là phải hiểu các thuật ngữ này:

1. Xử lý Đồng thời (Concurrency):

  • Định nghĩa: Khả năng của một hệ thống xử lý nhiều tác vụ bằng cách xen kẽ việc thực thi của chúng (không nhất thiết phải cùng một lúc).
  • Ví dụ: Chuyển đổi giữa việc xử lý một truy vấn cơ sở dữ liệu và xử lý việc tải lên tệp trong một vòng lặp sự kiện.

2. Xử lý Song song (Parallelism):

  • Định nghĩa: Thực hiện nhiều tác vụ đồng thời bằng cách tận dụng bộ xử lý đa lõi.
  • Ví dụ: Thực hiện các phép tính toán học phức tạp trên các lõi khác nhau đồng thời.

VD: Hãy tưởng tượng một nhà hàng:

  • Xử lý Đồng thời: Một đầu bếp duy nhất đa nhiệm giữa nhiều món ăn.
  • Xử lý Song song: Nhiều đầu bếp làm việc trên các món ăn riêng biệt cùng một lúc.

Xử lý Đồng thời trong TypeScript

JavaScript, và mở rộng là TypeScript, chạy trên một vòng lặp sự kiện đơn luồng, điều này có thể khiến xử lý đồng thời nghe có vẻ bất khả thi. Tuy nhiên, xử lý đồng thời đạt được thông qua các mô hình lập trình không đồng bộ như callbacks, promises, và async/await.

1. Sử dụng Promises cho Xử lý Đồng thời

Promises là một trong những cách đơn giản nhất để đạt được xử lý đồng thời trong 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();

Giải thích: Promise.all cho phép cả hai thao tác tìm nạp chạy đồng thời, tiết kiệm thời gian.

2. Xử lý Đồng thời với Async/Await

async/await đơn giản hóa việc xâu chuỗi promise trong khi vẫn duy trì tính chất không đồng bộ.

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();

Xử lý Song song trong TypeScript

Mặc dù JavaScript không hỗ trợ đa luồng một cách nguyên bản, Web Workers và Node.js Worker Threads cho phép xử lý song song. Các tính năng này tận dụng các luồng riêng biệt để xử lý các tác vụ tốn kém về mặt tính toán.

1. Web Workers cho Xử lý Song song

Trong môi trường trình duyệt, Web Workers thực thi các script trong một luồng riêng biệt.

// 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 Worker Threads

Đối với các ứng dụng phía máy chủ, Node.js cung cấp 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]);

Các mẫu cho Xử lý Đồng thời và Song song hiệu quả

1. Hàng đợi Tác vụ để Quản lý Xử lý Đồng thời

Khi xử lý nhiều tác vụ, hàng đợi tác vụ đảm bảo việc thực thi được kiểm soát.

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. Cân bằng Tải với Workers Pool

Workers Pool phân phối hiệu quả các tác vụ trên nhiều workers.

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); });
}

Thách thức và Giải pháp

1. Gỡ lỗi Mã Không đồng bộ

  • Sử dụng các công cụ như async_hooks trong Node.js để theo dõi các hoạt động không đồng bộ.
  • Sử dụng IDE hỗ trợ gỡ lỗi mã async/await.

2. Xử lý lỗi

Bọc promises trong các khối try/catch hoặc sử dụng .catch() với Promise.all.

3. Điều kiện Race

Tránh trạng thái chia sẻ hoặc sử dụng cơ chế khóa.

Best practice cho Xử lý Đồng thời và Song song

  1. Ưu tiên I/O Không đồng bộ: Tránh chặn luồng chính cho các hoạt động I/O nặng.

  2. Sử dụng Worker Threads cho các Tác vụ Chuyên sâu về CPU: Dỡ bỏ các phép tính nặng sang worker threads hoặc Web Workers.

  3. Giới hạn Xử lý Đồng thời: Sử dụng hàng đợi tác vụ hoặc các thư viện như p-limit để kiểm soát mức độ xử lý đồng thời.

  4. Tận dụng các Thư viện: Sử dụng các thư viện như Bull cho hàng đợi tác vụ hoặc Workerpool để quản lý worker thread.

Kết luận

Xử lý đồng thời và song song rất quan trọng để xây dựng các ứng dụng TypeScript hiệu suất cao, có khả năng mở rộng. Trong khi xử lý đồng thời cải thiện khả năng phản hồi bằng cách xen kẽ các tác vụ, xử lý song song cho phép thực thi đồng thời trên các hệ thống đa lõi. Bằng cách nắm vững các khái niệm này, các nhà phát triển có thể giải quyết các thách thức trong các ứng dụng hiện đại và mang lại trải nghiệm người dùng liền mạch.

Bình luận

Bài viết tương tự

- vừa được xem lúc

Giới thiệu Typescript - Sự khác nhau giữa Typescript và Javascript

Typescript là gì. TypeScript là một ngôn ngữ giúp cung cấp quy mô lớn hơn so với JavaScript.

0 0 528

- vừa được xem lúc

Type annotation vs Type Inference - Typescript

Trong bài viết này, chúng ta sẽ tìm hiểu kỹ về TypeScript bằng cách tìm hiểu sự khác biệt giữa kiểu chú thích và kiểu suy luận. Tôi sẽ cho rằng bạn có một số kinh nghiệm về JavaScript và biết về các kiểu cơ bản, như chuỗi, số và boolean.

0 0 45

- vừa được xem lúc

Type Annotation và Type Inference trong TypeScript là gì?

Khi làm việc với javascript chắc hẳn các bạn đã quá quen với việc dùng biến mà không cần phải quan tâm đến kiểu dữ liệu của nó là gì phải không? Đúng là mới đầu tiếp cận với Typescript mình cũng cảm thấy nó khá là phiền vì cần phải khai báo đủ type để nó chặt chẽ hơn. Lúc đó mình còn nghĩ: " JavaScr

0 0 37

- vừa được xem lúc

Tìm hiểu TypeScript và kiến thức cơ bản

TypeScript là gì. TypeScript sử dụng tất cả các tính năng của của ECMAScript 2015 (ES6) như classes, modules.

0 0 53

- vừa được xem lúc

TypeScript - P1: Vì sao TypeScript được yêu thích đến vậy?

Trải nghiệm thực tế. Trước khi là một Web Developer, tôi là một Mobile Developer và Java là thứ mà tôi từng theo đuổi.

0 1 69

- vừa được xem lúc

4 Tính năng rất hay từ TypeScript

Xin chào các bạn hôm nay mình xin chia sẽ những tính năng rất hay của TypeScript (TS), các bạn cùng tìm hiểu nhé. Ngoài việc set Type cho biến, tham số hay function thì ví dụ khi bạn nhìn vào một tham

0 0 96