Từ Javascript thuần đến RxJS (Phần 1)
Javascript, ES5, ES6, Functional Programming, Callback, Callback-Hell, Async, Sync, Blocking, Non-Blocking, Anonymous Function, Arrow Function, Higher Order Function, Promises, Async/Await, Observables, Observer, Reactive-Extension RxJS... Bạn quan tâm và muốn hiểu rõ về các khái niệm trên ...
Javascript, ES5, ES6, Functional Programming, Callback, Callback-Hell, Async, Sync, Blocking, Non-Blocking, Anonymous Function, Arrow Function, Higher Order Function, Promises, Async/Await, Observables, Observer, Reactive-Extension RxJS... Bạn quan tâm và muốn hiểu rõ về các khái niệm trên đây, muốn chạy thử để xem cách nó hoạt động ra sao? Bạn hãy đọc bài này.
Bài viết này sẽ tổng hợp và giải thích một số khái niệm từ dễ hiểu đến khó hiểu của Javascript, hi vọng sẽ giúp các bạn hiểu rõ hơn và áp dụng được vào dự án code của các bạn. Vì là dành cho những người thậm chí còn chưa biết gì về Javascript nên có nhiều chỗ tôi giải thích khá "tỉ mẩn", hướng dẫn từng bước một cách quá đáng, nên các bạn biết rồi thì cứ bỏ qua nhé.
Trước tiên, phải nhắc lại định nghĩa một chút, Javascript là một ngôn ngữ lập trình hàm. Lập trình hàm hay Functional Programming có cách viết code khác so với các ngôn ngữ lập trình Hướng đối tượng (Object Oriented Programming - OOP) như C# hoặc Java, C++. Lập trình hàm tức là mọi thứ đều là hàm. Khi viết code bằng Javascript thường thì chúng ta chỉ viết hàm, và sử dụng các biến (các object) để chứa dữ liệu. Chỉ cần 2 khái niệm HÀM và BIẾN thôi là chúng ta đã bắt đầu code được. Khá dễ dàng phải không.
Javascript cũng chạy được ngay trên trình duyệt, vì thế bạn không cần cài gì để bắt đầu học js, chỉ có trình duyệt web là có thể nghịch Javascript. Tôi thì hay dùng Chrome nhưng các bạn có thể dùng Firefox hoặc Safari, Opera, Edge...đều được.
Tôi sẽ viết thử một ví dụ code bằng javascript như sau:
function ShowNum1(){ console.log('1'); } function ShowNum2(){ console.log('2'); } function ShowNum3(){ console.log('3'); } ShowNum1(); //dòng lệnh 1 cần thực thi ShowNum2(); //dòng lệnh 2 cần thực thi ShowNum3(); //dòng lệnh 3 cần thực thi
Để chạy thử ví dụ này chúng ta hãy sử dùng Chrome, bật chế độ Debug lên bằng cách ấn F12. Rồi ở màn hình Development Mode, ta vào tab Console. Sau đó có thể paste code Javascript trực tiếp vào cửa sổ này và Enter để xem kết quả.
Chúng ta sẽ nhận được 3 giá trị hiển thị lần lượt là 1, 2, 3.
Javascript thường thì khi chạy code sẽ thực thi tuần tự hoặc còn gọi là thực thi đồng bộ (synchronous, viết tắt là sync). Tức là, các lệnh được thực thi lần lượt từ trên xuống dưới.
Hỏi ngu:Chạy tuần tự được, thế có chạy không tuần tự được không?
Được. Giờ nếu code Javascript chạy theo kiểu không tuần tự, hay còn gọi là bất đồng bộ (asynchronous, viết tắt là async) thì chúng ta sửa lại như sau:
function ShowNum1(){ console.log('1'); } function ShowNum2(){ console.log('2'); } function ShowNum3(){ console.log('3'); } ShowNum1(); //dòng lệnh 1 cần thực thi setTimeout(ShowNum2, 2000); //dòng lệnh 2 cần thực thi ShowNum3(); //dòng lệnh 3 cần thực thi
Chúng ta sẽ nhận được 3 giá trị 1, 3, 2.
Hỏi ngu: Tại sao lại như vậy nhỉ? WHY ?!?
Dòng lệnh thứ 3 đã chạy trước rồi dòng thứ 2 mới chạy sau. Như vậy nghĩa là không tuần tự theo thứ tự hàm viết từ trên xuống nữa. Điều này xảy ra được là vì dòng lệnh thứ 2 chạy ngầm. Nó vẫn đang chạy và 2 giây sau nó trả về kết quả, còn dòng thứ 3 không phải đợi dòng 2 chạy xong rồi mới chạy. Mà gần như 2 dòng 2 và 3 chạy đồng thời (Ta gọi đó là chạy bất đồng bộ, hay là chạy kiểu async)
Hỏi ngu: Làm thế nào mà hàm setTimeout() lại chạy ngầm được?
Là vì Javascript có những hàm thực thi theo chế độ non-blocking (không chặn các hàm khác, hay không làm chương trình bị đơ). Con trỏ hàm sẽ chạy tiếp lệnh tiếp theo chứ toàn bộ chương trình không đợi từng lệnh được thực thi. Hàm console.log() cũng là một hàm non-blocking.
Hỏi ngu: Hàm non-blocking à, thế thì phải có cả hàm blocking chứ nhỉ?
Đúng vậy, Javascript có cả các hàm Blocking. Chúng ta hãy thử viết lại ví dụ lúc nãy. Trong ShowNum1() tôi thay hàm console.log() bằng hàm alert():
function ShowNum1(){ alert('1'); } function ShowNum2(){ console.log('2'); } function ShowNum3(){ console.log('3'); } ShowNum1(); //dòng lệnh 1 cần thực thi setTimeout(ShowNum2, 2000); //dòng lệnh 2 cần thực thi ShowNum3(); //dòng lệnh 3 cần thực thi
Sau khi chạy thử ta thấy cửa sổ bật lên hiện ra số 1, trình duyệt cứ đơ ở đó và màn hình dòng lệnh không thấy chạy tiếp. Chỉ khi nhấn OK thì console mới hiện ra số 3, rồi số 2. Alert chính là một hàm Blocking I/O (chặn đầu ra đầu vào của chương trình thực thi javascript, ở đây có thể hiểu là trình duyệt Chrome bị chặn) dẫn đến Chrome được lệnh phải đợi lệnh gọi hàm alert() chạy xong mới đc chạy tiếp dòng lệnh tiếp theo.
Ghi chú: Đối với Javascript và NodeJS thì hầu hết các hàm đều là dưới dạng Non-Blocking (async) để không làm gián đoạn trải nghiệm của người dùng.
Hỏi ngu: Có vẻ như Blocking chính là Sync và Non-Blocking chính là Async?
Đúng vậy, bạn đã hiểu đúng vấn đề.
Hỏi ngu: Thế sao lại phải có 2 cách gọi làm gì nhỉ?
Vì gọi Async (Ây-Xuynh) và Sync (Xuynh) nghe nó sang mồm hơn