Đây là một chia sẻ rất hay và giúp chúng ta hiểu sâu hơn về cách hoạt động của JavaScript. Mình sẽ giải thích lý do tại sao kết quả lại như vậy:
Mặc dù setTimeout() được coi là bất đồng bộ, nhưng JavaScript vẫn thực thi code theo mô hình đơn luồng (single-threaded).
Khi gặp setTimeout(), JavaScript sẽ đẩy callback function vào Web APIs (trong trình duyệt) hoặc libuv (trong Node.js) để xử lý. Đồng thời, một bộ đếm thời gian được khởi động.
Trong khi chờ đợi, JavaScript tiếp tục thực thi các dòng code tiếp theo, trong trường hợp này là vòng lặp for.
Khi bộ đếm thời gian kết thúc (sau 10ms), callback function không được thực thi ngay lập tức. Thay vào đó, nó được đẩy vào Callback Queue (hoặc Task Queue).
Event Loop liên tục kiểm tra xem Call Stack có trống không. Nếu trống, nó sẽ lấy function đầu tiên từ Callback Queue và đưa vào Call Stack để thực thi.
Trong ví dụ của bạn, vòng lặp for chiếm toàn bộ Call Stack trong một thời gian dài (có thể hơn 10ms). Trong suốt thời gian này, Event Loop không thể đưa callback function từ setTimeout() vào Call Stack được.
Chỉ khi vòng lặp for kết thúc, Call Stack trống, Event Loop mới có thể đưa callback function vào để thực thi.
Đó là lý do tại sao bạn luôn thấy dòng log "timeout" ở cuối cùng. Mặc dù setTimeout() là bất đồng bộ, nhưng việc thực thi callback function vẫn phải tuân theo quy tắc của Event Loop và đợi cho đến khi Call Stack trống.
Điều này cho thấy rằng "bất đồng bộ" trong JavaScript không có nghĩa là "chạy song song", mà là "không chặn" (non-blocking) - cho phép code tiếp tục thực thi trong khi chờ đợi các tác vụ khác hoàn thành.
Bạn nói đúng khi cho rằng sửa đổi prototype sẽ ghi đè (overwrite) các phương thức hiện có. Điều này có thể dẫn đến việc mất đi các chức năng đã được định nghĩa trước đó trên prototype.
Việc ghi đè prototype có thể gây ra lỗi ở những nơi khác trong code nếu có logic phụ thuộc vào các phương thức cũ. Đây là một rủi ro cần lưu ý khi thực hiện việc này.
Cách tiếp cận an toàn hơn, như bạn đề xuất, là kiểm tra xem phương thức đã tồn tại chưa trước khi định nghĩa nó. Ví dụ:
if(typeofMyObject.prototype.myMethod ==='undefined'){MyObject.prototype.myMethod=function(){// Định nghĩa phương thức mới};}else{
console.warn('Phương thức myMethod đã tồn tại trên prototype');}
Nếu phương thức đã tồn tại, ta có thể chọn giữ nguyên phương thức cũ, ghi log cảnh báo, hoặc throw một error tùy thuộc vào yêu cầu cụ thể của ứng dụng.
Một cách tiếp cận khác là sử dụng Object.defineProperty() để thêm hoặc sửa đổi phương thức trên prototype một cách an toàn hơn:
Object.defineProperty(MyObject.prototype,'myMethod',{value:function(){// Định nghĩa phương thức mới},
writable:true,
configurable:true,
enumerable:false});
Thực ra trong dự án thì thường sẽ có setup ESLint , việc sử dụng các công cụ linting và kiểm tra tĩnh có thể giúp phát hiện các thay đổi không mong muốn đối với prototype.
Việc kiểm tra trước khi định nghĩa và sử dụng các phương pháp an toàn để thêm hoặc sửa đổi phương thức là những cách tiếp cận tốt để tránh các vấn đề tiềm ẩn trong code.
Cảm ơn bạn @refacore, mình không ngờ lại nhận được nhiều commnet như vậy. Có nhiều comment thật sự thấy bài viết sôi động hẳn. Cảm ơn bạn và trong tương lai nếu có thể mong bạn comment thật nhiều để giúp mình cải thiện chất lương bài viết nhé. (Viết mà ko thấy ai comment thì cũng chán lắm, năm ngoái mình bỏ viết hơn 1 năm giờ mới viết lại đấy kaka ).
À quay lại câu hỏi về continue và break trong vòng lặp.
Về continue:
continue có một số điểm đặc biệt trong vòng lặp:
Nó cho phép bỏ qua phần còn lại của mã trong lần lặp hiện tại và chuyển đến lần lặp tiếp theo.
Khi gặp continue, vòng lặp sẽ ngay lập tức chuyển đến bước tiếp theo (ví dụ: tăng biến đếm trong vòng lặp for) mà không thực hiện các câu lệnh còn lại trong thân vòng lặp.
continue rất hữu ích khi bạn muốn bỏ qua một số trường hợp cụ thể trong vòng lặp mà không muốn kết thúc toàn bộ vòng lặp.
Ví dụ:
for(let i =0; i <5; i++){if(i ===2)continue;
console.log(i);}// Output: 0, 1, 3, 4
Về break:
break cũng có những đặc điểm riêng:
Nó sẽ kết thúc hoàn toàn vòng lặp và chuyển điều khiển chương trình ra khỏi vòng lặp.
Khi gặp break, chương trình sẽ thoát khỏi vòng lặp ngay lập tức, bỏ qua tất cả các lần lặp còn lại.
break thường được sử dụng khi bạn muốn thoát khỏi vòng lặp khi đã tìm thấy kết quả mong muốn hoặc khi gặp một điều kiện đặc biệt nào đó.
Ví dụ:
for(let i =0; i <5; i++){if(i ===3)break;
console.log(i);}// Output: 0, 1, 2
Điểm khác biệt chính giữa continue và break là:
continue chỉ bỏ qua lần lặp hiện tại và tiếp tục với lần lặp tiếp theo.
break kết thúc toàn bộ vòng lặp và thoát ra ngoài.
Cả hai đều là công cụ mạnh mẽ để kiểm soát luồng thực thi trong vòng lặp, giúp code của bạn linh hoạt và hiệu quả hơn.
Hy vọng những giải thích này giúp các bạn đọc hiểu rõ hơn về continue và break. Nếu bạn có bất kỳ câu hỏi nào khác, đừng ngần ngại hỏi nhé!
Cảm ơn bạn @refacore đã đọc bài viết và đưa ra câu hỏi rất hay! Đây là một điểm thú vị trong JavaScript mà mình rất vui được giải thích.
Bạn hoàn toàn đúng về kết quả của đoạn code. Hãy cùng phân tích từng block nhé:
Block 1:
{var message ='test';
console.log(window.message);// test
console.log(this.message);// test}
Trong block này, var được sử dụng để khai báo biến message. Biến được khai báo bằng var có phạm vi function hoặc global, không bị giới hạn bởi block. Do đó, nó được thêm vào đối tượng global (window trong trình duyệt) và có thể truy cập thông qua window.message hoặc this.message (khi this trỏ đến global object).
Trong block này, const được sử dụng để khai báo biến message. Khác với var, biến được khai báo bằng const (cũng như let) có phạm vi block. Điều này có nghĩa là biến message chỉ tồn tại trong block đó và không được thêm vào đối tượng global. Vì vậy, cả window.message và this.message đều trả về undefined.
Đây là một ví dụ tuyệt vời về sự khác biệt giữa var và const/let trong JavaScript. Nó cho thấy tại sao việc sử dụng const và let có thể giúp tránh ô nhiễm phạm vi global và tạo ra code an toàn hơn.
Cảm ơn bạn một lần nữa vì câu hỏi rất hay này. Những tương tác như thế này thực sự là động lực để mình tiếp tục viết nhiều bài hơn nữa trong tương lai. Nếu bạn có bất kỳ câu hỏi nào khác, đừng ngần ngại hỏi nhé!
sửa prototype thì nó overwrite, tức là không có xung đột, mà có thể break code ở đâu đó do logic đã cập nhật lại. Cách thường làm là ta check xem func đấy đã có chưa trước khi khai báo nó. Nếu có rồi thì dùng lại hoặc throw hoặc log error.
Airflow không chỉ dùng để lập lịch chạy hàm trong database, mà còn dùng trong dự án bình thường. Dễ thấy nhất trong các dự án to, có ~1000 task như Spotify thì họ sẽ dùng Airflow hoặc 1 trình quản lý khác như Luigi mà họ tạo ra để quản lý các task, visualize các luồng tasks, nói đơn giản là 1 cái pipeline ý.
struct là kiểu giá trị. vì thế trong struct ko dùng string mà dùng mảng char, do string là kiểu tham chiếu không có độ dài cố định, không thể để trong struct. cũng vì thế struct có size cố định. là kiểu giá trị nên struct được clone ra khi truyền vào function. thay đổi struct ở local scope không ảnh hưởng outer scope.
À bạn có thể cho mình xin dockerfile các image trong bài viêtd ko? Do mình test rewrite-target của ingress, cần điều chỉnh thử root folder của frontend. Nếu được mình cảm ơn nhé
THẢO LUẬN
Cảm ơn bạn @refacore đã chia sẻ ví dụ này.
Đây là một chia sẻ rất hay và giúp chúng ta hiểu sâu hơn về cách hoạt động của JavaScript. Mình sẽ giải thích lý do tại sao kết quả lại như vậy:
Mặc dù setTimeout() được coi là bất đồng bộ, nhưng JavaScript vẫn thực thi code theo mô hình đơn luồng (single-threaded).
Khi gặp setTimeout(), JavaScript sẽ đẩy callback function vào Web APIs (trong trình duyệt) hoặc libuv (trong Node.js) để xử lý. Đồng thời, một bộ đếm thời gian được khởi động.
Trong khi chờ đợi, JavaScript tiếp tục thực thi các dòng code tiếp theo, trong trường hợp này là vòng lặp for.
Khi bộ đếm thời gian kết thúc (sau 10ms), callback function không được thực thi ngay lập tức. Thay vào đó, nó được đẩy vào Callback Queue (hoặc Task Queue).
Event Loop liên tục kiểm tra xem Call Stack có trống không. Nếu trống, nó sẽ lấy function đầu tiên từ Callback Queue và đưa vào Call Stack để thực thi.
Trong ví dụ của bạn, vòng lặp for chiếm toàn bộ Call Stack trong một thời gian dài (có thể hơn 10ms). Trong suốt thời gian này, Event Loop không thể đưa callback function từ setTimeout() vào Call Stack được.
Chỉ khi vòng lặp for kết thúc, Call Stack trống, Event Loop mới có thể đưa callback function vào để thực thi.
Đó là lý do tại sao bạn luôn thấy dòng log "timeout" ở cuối cùng. Mặc dù setTimeout() là bất đồng bộ, nhưng việc thực thi callback function vẫn phải tuân theo quy tắc của Event Loop và đợi cho đến khi Call Stack trống.
Điều này cho thấy rằng "bất đồng bộ" trong JavaScript không có nghĩa là "chạy song song", mà là "không chặn" (non-blocking) - cho phép code tiếp tục thực thi trong khi chờ đợi các tác vụ khác hoàn thành.
Cảm ơn vì bổ sung nhé @refacore,
Mình xin phép làm rõ thêm ý kiến của bạn:
Bạn nói đúng khi cho rằng sửa đổi prototype sẽ ghi đè (overwrite) các phương thức hiện có. Điều này có thể dẫn đến việc mất đi các chức năng đã được định nghĩa trước đó trên prototype.
Việc ghi đè prototype có thể gây ra lỗi ở những nơi khác trong code nếu có logic phụ thuộc vào các phương thức cũ. Đây là một rủi ro cần lưu ý khi thực hiện việc này.
Cách tiếp cận an toàn hơn, như bạn đề xuất, là kiểm tra xem phương thức đã tồn tại chưa trước khi định nghĩa nó. Ví dụ:
Nếu phương thức đã tồn tại, ta có thể chọn giữ nguyên phương thức cũ, ghi log cảnh báo, hoặc throw một error tùy thuộc vào yêu cầu cụ thể của ứng dụng.
Một cách tiếp cận khác là sử dụng Object.defineProperty() để thêm hoặc sửa đổi phương thức trên prototype một cách an toàn hơn:
Việc kiểm tra trước khi định nghĩa và sử dụng các phương pháp an toàn để thêm hoặc sửa đổi phương thức là những cách tiếp cận tốt để tránh các vấn đề tiềm ẩn trong code.
Cảm ơn bạn @refacore, mình không ngờ lại nhận được nhiều commnet như vậy. Có nhiều comment thật sự thấy bài viết sôi động hẳn. Cảm ơn bạn và trong tương lai nếu có thể mong bạn comment thật nhiều để giúp mình cải thiện chất lương bài viết nhé. (Viết mà ko thấy ai comment thì cũng chán lắm, năm ngoái mình bỏ viết hơn 1 năm giờ mới viết lại đấy kaka ).
À quay lại câu hỏi về
continue
vàbreak
trong vòng lặp.Về
continue
:continue
có một số điểm đặc biệt trong vòng lặp:continue
, vòng lặp sẽ ngay lập tức chuyển đến bước tiếp theo (ví dụ: tăng biến đếm trong vòng lặp for) mà không thực hiện các câu lệnh còn lại trong thân vòng lặp.continue
rất hữu ích khi bạn muốn bỏ qua một số trường hợp cụ thể trong vòng lặp mà không muốn kết thúc toàn bộ vòng lặp.Ví dụ:
Về
break
:break
cũng có những đặc điểm riêng:break
, chương trình sẽ thoát khỏi vòng lặp ngay lập tức, bỏ qua tất cả các lần lặp còn lại.break
thường được sử dụng khi bạn muốn thoát khỏi vòng lặp khi đã tìm thấy kết quả mong muốn hoặc khi gặp một điều kiện đặc biệt nào đó.Ví dụ:
Điểm khác biệt chính giữa
continue
vàbreak
là:continue
chỉ bỏ qua lần lặp hiện tại và tiếp tục với lần lặp tiếp theo.break
kết thúc toàn bộ vòng lặp và thoát ra ngoài.Cả hai đều là công cụ mạnh mẽ để kiểm soát luồng thực thi trong vòng lặp, giúp code của bạn linh hoạt và hiệu quả hơn.
Hy vọng những giải thích này giúp các bạn đọc hiểu rõ hơn về
continue
vàbreak
. Nếu bạn có bất kỳ câu hỏi nào khác, đừng ngần ngại hỏi nhé!😋😋😋😋
Cảm ơn bạn @refacore đã đọc bài viết và đưa ra câu hỏi rất hay! Đây là một điểm thú vị trong JavaScript mà mình rất vui được giải thích.
Bạn hoàn toàn đúng về kết quả của đoạn code. Hãy cùng phân tích từng block nhé:
Block 1:
Trong block này,
var
được sử dụng để khai báo biếnmessage
. Biến được khai báo bằngvar
có phạm vi function hoặc global, không bị giới hạn bởi block. Do đó, nó được thêm vào đối tượng global (window
trong trình duyệt) và có thể truy cập thông quawindow.message
hoặcthis.message
(khithis
trỏ đến global object).Block 2:
Trong block này,
const
được sử dụng để khai báo biếnmessage
. Khác vớivar
, biến được khai báo bằngconst
(cũng nhưlet
) có phạm vi block. Điều này có nghĩa là biếnmessage
chỉ tồn tại trong block đó và không được thêm vào đối tượng global. Vì vậy, cảwindow.message
vàthis.message
đều trả vềundefined
.Đây là một ví dụ tuyệt vời về sự khác biệt giữa
var
vàconst
/let
trong JavaScript. Nó cho thấy tại sao việc sử dụngconst
vàlet
có thể giúp tránh ô nhiễm phạm vi global và tạo ra code an toàn hơn.Cảm ơn bạn một lần nữa vì câu hỏi rất hay này. Những tương tác như thế này thực sự là động lực để mình tiếp tục viết nhiều bài hơn nữa trong tương lai. Nếu bạn có bất kỳ câu hỏi nào khác, đừng ngần ngại hỏi nhé!
cám ơn nhận xét của bạn ạ
cảm ơn nhiều nhé
bài viết khá hay ạ. 10đ cho tác giả
5 thử đoạn code này:
sẽ luôn thấy dòng log timeout ở cuối, tức là nó phải đợi vòng lặp for xong mới thực thi. Nếu là bất đồng bộ thì tại sao?
4 có thể check bằng Object.keys(a).
sửa prototype thì nó overwrite, tức là không có xung đột, mà có thể break code ở đâu đó do logic đã cập nhật lại. Cách thường làm là ta check xem func đấy đã có chưa trước khi khai báo nó. Nếu có rồi thì dùng lại hoặc throw hoặc log error.
continue trong vòng lặp có gì đặc biệt? break thì sao ^^!
Sửa lại đoạn code đầu tiên thành thế này:
block 1 không đổi. kết quả block 2 đều là undefined. Em giải thích xem?
Airflow không chỉ dùng để lập lịch chạy hàm trong database, mà còn dùng trong dự án bình thường. Dễ thấy nhất trong các dự án to, có ~1000 task như Spotify thì họ sẽ dùng Airflow hoặc 1 trình quản lý khác như Luigi mà họ tạo ra để quản lý các task, visualize các luồng tasks, nói đơn giản là 1 cái pipeline ý.
struct là kiểu giá trị. vì thế trong struct ko dùng string mà dùng mảng char, do string là kiểu tham chiếu không có độ dài cố định, không thể để trong struct. cũng vì thế struct có size cố định. là kiểu giá trị nên struct được clone ra khi truyền vào function. thay đổi struct ở local scope không ảnh hưởng outer scope.
bạn xem source ở đây nhé: https://github.com/maitrungduc1410/todo-app
Cảm ơn a đã góp ý e mới học nên nhiều cái chưa rõ ạ
À bạn có thể cho mình xin dockerfile các image trong bài viêtd ko? Do mình test rewrite-target của ingress, cần điều chỉnh thử root folder của frontend. Nếu được mình cảm ơn nhé
sao cứ viết kiểu nửa Tây nửa ta đọc khó hiểu thật ấy
cho mình ké link này nha: https://cookkitmart.com/