1. Lập trình bất đồng bộ là gì?

Lập trình bất đồng bộ (Asynchronous Programming) là cách tổ chức chương trình cho phép một tác vụ mất thời gian được thực hiện mà không cần chặn toàn bộ chương trình trong thời gian chờ.

Trong ứng dụng thực tế, có nhiều công việc cần thời gian để hoàn thành như:

  • Gọi dữ liệu từ API hoặc Internet.
  • Đọc và ghi dữ liệu vào Database.
  • Đọc dữ liệu từ File.
  • Gửi dữ liệu lên Server.
  • Thực hiện các tác vụ có thời gian chờ.

Nếu chương trình phải chờ từng tác vụ hoàn thành mới tiếp tục, ứng dụng có thể trở nên chậm hoặc không phản hồi.

Vì vậy, Dart cung cấp cơ chế lập trình bất đồng bộ thông qua các khái niệm như Future, async và await.

  1. Synchronous và Asynchronous

Synchronous

Synchronous là xử lý đồng bộ, các công việc được thực hiện theo thứ tự. Chương trình phải chờ công việc hiện tại hoàn thành trước khi chuyển sang công việc tiếp theo.

Có thể hiểu:

Công việc A → hoàn thành → Công việc B → hoàn thành → Công việc C

Nếu A mất nhiều thời gian thì B phải chờ A.

Asynchronous

Asynchronous là xử lý bất đồng bộ, cho phép chương trình tiếp tục thực hiện những công việc khác trong khi một tác vụ mất thời gian đang được xử lý.

Có thể hiểu:

Bắt đầu A → A đang xử lý → chương trình có thể tiếp tục công việc khác → A hoàn thành → xử lý kết quả của A

Trong Flutter, lập trình bất đồng bộ rất quan trọng vì ứng dụng thường xuyên phải làm việc với API, Database, File và các dịch vụ bên ngoài.

  1. Future là gì?

Future là một đối tượng trong Dart dùng để đại diện cho một kết quả sẽ có trong tương lai.

Khi một hàm thực hiện một công việc bất đồng bộ, nó thường không trả về kết quả ngay lập tức mà trả về một Future.

Ví dụ:

Future<String> getData() async {
  return "Dữ liệu";
}

Future<String> có nghĩa là hàm sẽ cung cấp một giá trị String sau khi tác vụ hoàn thành.

Future thường được sử dụng cho các tác vụ như gọi API, đọc Database, đọc File hoặc các công việc có thời gian chờ.

  1. Trạng thái của Future

Một Future có thể được hiểu thông qua hai trạng thái chính:

Uncompleted

  • Future chưa hoàn thành.
  • Tác vụ bất đồng bộ vẫn đang được thực hiện và kết quả chưa có.
  • Ví dụ khi ứng dụng đang gọi API thì Future có thể đang ở trạng thái chưa hoàn thành.

Completed

  • Future đã hoàn thành.
  • Khi hoàn thành, Future có thể: Trả về kết quả thành công, hoặc hoàn thành với một lỗi.

Có thể hiểu luồng chuyển đổi như sau:

Future → Uncompleted → Completed

Sau khi hoàn thành:
Completed → Value
hoặc:
Completed → Error
  1. Future<T>

Future<T> sử dụng kiểu T để xác định kiểu dữ liệu mà Future sẽ trả về.

Một số dạng thường gặp:

Future<String>
Future<int>
Future<double>
Future<User>
Future<List<Product>>

Ví dụ Future<String> có nghĩa là sau khi hoàn thành, Future sẽ cung cấp một giá trị kiểu String.

Nếu một tác vụ không cần trả về dữ liệu thì có thể sử dụng:

Future<void>

Future<void> thường được sử dụng cho những tác vụ như lưu, cập nhật hoặc xóa dữ liệu mà không cần trả về một giá trị cụ thể.

  1. async là gì?

async là từ khóa dùng để đánh dấu một hàm là hàm bất đồng bộ.

Khi một hàm được đánh dấu async, hàm đó thường trả về một Future.

Ví dụ:

Future<String> getName() async {
  return "Thương";
}

async cho phép sử dụng await bên trong hàm để chờ các tác vụ bất đồng bộ hoàn thành.

Có thể hiểu: async → khai báo hàm bất đồng bộ.

  1. await là gì?

await được sử dụng để chờ một Future hoàn thành và lấy kết quả của Future đó.

Ví dụ:

final data = await getData();

Ở đây chương trình sẽ chờ getData() hoàn thành trước khi tiếp tục xử lý dòng code tiếp theo trong hàm bất đồng bộ.

await thường được sử dụng bên trong hàm có async.

Có thể hiểu:

  • Future → đại diện cho kết quả trong tương lai
  • await → chờ Future hoàn thành để lấy kết quả
  1. Mối quan hệ giữa Future, async và await

Ba khái niệm này thường đi cùng nhau:

  • Future → đại diện cho kết quả sẽ có trong tương lai.
  • async → đánh dấu một hàm có xử lý bất đồng bộ.
  • await → chờ một Future hoàn thành và lấy kết quả.

Một luồng xử lý đơn giản có thể hiểu là:

Gọi hàm async → nhận Future → await chờ Future → nhận kết quả → tiếp tục xử lý

Đây là cách viết bất đồng bộ phổ biến trong Dart và Flutter.

  1. async/await giúp code dễ đọc hơn

Trước khi có async/await, việc xử lý nhiều tác vụ bất đồng bộ có thể sử dụng các phương thức như .then() và callback.

Khi có nhiều tác vụ liên tiếp, cách viết này có thể khiến code trở nên khó đọc.

async/await cho phép lập trình viên viết luồng xử lý gần giống với cách chương trình xử lý tuần tự:

Gọi API → chờ kết quả → xử lý dữ liệu → lưu dữ liệu → tiếp tục

Nhờ đó, code bất đồng bộ dễ đọc, dễ hiểu và dễ bảo trì hơn.

  1. Xử lý lỗi với async/await

Các tác vụ bất đồng bộ không phải lúc nào cũng thành công. Ví dụ như mất kết nối Internet, Server không phản hồi, API trả về lỗi, Database xảy ra lỗi, hoặc dữ liệu trả về không hợp lệ.

Khi sử dụng async/await, có thể xử lý lỗi bằng try-catch:

try {
  final data = await getData();
} catch (e) {
  // xử lý lỗi
}

Nếu Future hoàn thành thành công, chương trình tiếp tục xử lý kết quả.

Nếu Future xảy ra lỗi, chương trình có thể chuyển sang catch để xử lý.

Việc xử lý lỗi rất quan trọng trong ứng dụng thực tế vì không thể giả định rằng mọi tác vụ bất đồng bộ đều thành công.

  1. Future trong Flutter

Future xuất hiện rất thường xuyên trong các ứng dụng Flutter. Một quy trình phổ biến khi lấy dữ liệu từ API là:

Flutter → gọi API → chờ Future → nhận Response → xử lý JSON → chuyển thành Model → cập nhật State → hiển thị giao diện

Ví dụ một Service có thể có dạng:

Future<List<Product>> getProducts() async {
  // gọi API
}

Điều này cho biết getProducts() sẽ trả về một List<Product> trong tương lai.

Vì vậy, hiểu Future và async/await là kiến thức nền tảng trước khi học các phần như HTTP, REST API, Dio và State Management.

  1. FutureBuilder trong Flutter

Flutter cung cấp FutureBuilder để xây dựng giao diện dựa trên trạng thái của một Future.

FutureBuilder thường được sử dụng để xử lý các trạng thái: Đang chờ → Có dữ liệu → Có lỗi.

Ví dụ khi gọi API lấy danh sách sản phẩm:

  • Loading → API hoàn thành → hiển thị danh sách
  • Nếu API xảy ra lỗi: Loading → API lỗi → hiển thị thông báo lỗi

FutureBuilder giúp kết nối quá trình xử lý Future với giao diện Flutter.

  1. Future và nhiều tác vụ bất đồng bộ

Trong một ứng dụng, có thể có nhiều Future cùng lúc.

Nếu các tác vụ không phụ thuộc vào nhau, có thể thực hiện chúng cùng thời điểm thay vì chờ từng tác vụ hoàn thành.

Dart cung cấp Future.wait() để chờ nhiều Future hoàn thành.

Có thể hiểu:

Future A + Future B + Future C → Future.wait() → tất cả hoàn thành → tiếp tục xử lý

Điều này có thể giúp giảm thời gian chờ khi các tác vụ hoàn toàn độc lập với nhau.

  1. Future không phải là Thread

Một điểm cần phân biệt là Future không phải Thread.

Future đại diện cho kết quả của một tác vụ sẽ hoàn thành trong tương lai. Trong khi đó, Thread là một cơ chế thực thi.

Dart sử dụng mô hình riêng dựa trên Event Loop, Future và Isolate để xử lý các công việc bất đồng bộ.

Vì vậy không nên hiểu đơn giản rằng: Future = một Thread chạy riêng.

Future chủ yếu dùng để quản lý và biểu diễn kết quả của các tác vụ bất đồng bộ.

  1. Future và Isolate

Future phù hợp với những công việc có thời gian chờ như: Gọi API, đọc Database, đọc File, Network I/O.

Tuy nhiên, nếu phải thực hiện một phép tính rất nặng, việc sử dụng Future không tự động khiến phép tính đó chạy trên một luồng xử lý riêng.

Dart cung cấp Isolate cho những trường hợp cần thực hiện các tác vụ tính toán nặng mà không muốn ảnh hưởng đến luồng xử lý giao diện.

Vì vậy:

  • Future & Async/Await → tập trung vào xử lý tác vụ bất đồng bộ.
  • Isolate & Concurrency → tập trung nhiều hơn vào việc thực hiện công việc độc lập và xử lý các tác vụ tính toán nặng.
  1. Những điểm cần nhớ

Khi học Future & Async/Await, cần nhớ những ý chính sau:

Synchronous   → thực hiện tuần tự và phải chờ công việc hiện tại hoàn thành.
Asynchronous  → cho phép chương trình tiếp tục xử lý trong khi tác vụ khác đang chờ.
Future        → đại diện cho một kết quả sẽ có trong tương lai.
Future<T>     → cho biết kiểu dữ liệu mà Future sẽ trả về.
async         → đánh dấu một hàm là bất đồng bộ.
await         → chờ Future hoàn thành và lấy kết quả.
try-catch     → dùng để xử lý lỗi trong quá trình thực hiện Future.
Future.wait() → dùng để chờ nhiều Future hoàn thành.
FutureBuilder → dùng Future để xây dựng và cập nhật giao diện Flutter theo trạng thái dữ liệu.
Không phải Thread → Future là cơ chế biểu diễn và xử lý kết quả bất đồng bộ, không phải một luồng thực thi riêng.
  1. Kết luận

Future và Async/Await là kiến thức nền tảng của Dart và được sử dụng rất nhiều trong Flutter.

Future đại diện cho một kết quả chưa có ngay tại thời điểm hiện tại. async được sử dụng để khai báo hàm bất đồng bộ, còn await dùng để chờ Future hoàn thành và lấy kết quả.

Trong quá trình làm Flutter, các kiến thức này đặc biệt quan trọng khi làm việc với API, Database, File, Firebase và các tác vụ có thời gian chờ.

Điều quan trọng nhất cần nhớ là:

Future = kết quả trong tương lai → async = hàm bất đồng bộ → await = chờ Future → nhận kết quả → xử lý dữ liệu hoặc lỗi

 

Bài viết khác

JSON & HTTP Status Codes

1. JSON là gì? JSON là một định dạng dữ liệu được sử dụng rất phổ biến trong lập trình, đặc biệt là khi Client và Server cần trao đổi dữ liệu với nhau. JSON có cách tổ chức dữ liệu khá đơn giản và dễ đọc. Dữ liệu thường được biểu diễn theo dạng […]

Tìm hiểu về Flutter Widget Fundamentals

  Widget trong Flutter là gì? Widget là thành phần cơ bản nhất được sử dụng để xây dựng giao diện trong Flutter. Mỗi thành phần trên màn hình như văn bản, hình ảnh, nút bấm, danh sách, khoảng cách hoặc toàn bộ một màn hình đều có thể được biểu diễn bằng Widget. Thay […]

Tìm hiểu về Isolate & Concurrency

  Isolate là gì? Trong Dart, Isolate là một vùng thực thi độc lập, có bộ nhớ riêng và Event Loop riêng. Mỗi Isolate có thể thực hiện công việc mà không trực tiếp chia sẻ bộ nhớ với Isolate khác. Có thể hiểu đơn giản: Isolate là cách Dart thực hiện các tác vụ […]

Tìm hiểu về Null Safety

  Null Safety là gì? Null Safety là một cơ chế an toàn kiểu dữ liệu được Dart sử dụng để hạn chế các lỗi liên quan đến giá trị null trong chương trình. Null Safety được đưa vào Dart nhằm giúp lập trình viên xác định rõ một biến có thể chứa null hay […]

TÌM HIỂU VỀ DART OO

  Dart OOP là gì? Dart OOP là lập trình hướng đối tượng (Object-Oriented Programming) trong ngôn ngữ Dart. Đây là phương pháp lập trình tổ chức chương trình xoay quanh đối tượng (Object) thay vì chỉ sử dụng các biến và hàm riêng lẻ. Trong OOP, một đối tượng thường có dữ liệu và […]

Tìm hiểu về ngôn ngữ lập trình Dart

  Dart là gì? Dart là một ngôn ngữ lập trình mã nguồn mở được phát triển bởi Google, được sử dụng để xây dựng các ứng dụng có thể chạy trên nhiều nền tảng khác nhau. Hiện nay, Dart được biết đến nhiều nhất vì đây là ngôn ngữ lập trình chính được sử […]

Leave a Reply

Your email address will not be published. Required fields are marked *