đoạn này e phải chú ý thật kĩ là ở cái new Promise e đặt await.
Khi e đặt await trước 1 cái Promise, thì Event loop sẽ chờ cho tới khi cái Promise đó được resolve thì nó mới tiếp tục chạy các phần code bên dưới (nhiều blog khác vẫn gọi là nó "pause execution" - dừng thực hiện chờ cho resolve thì mới "resume").
Cụ thể bài của e sẽ như sau:
đầu tiên async1(); được gọi -> thực hiện console.log("async1 start");
tiếp đó e gọi await async2() -> thực hiện console.log("async2 start")
tiếp await new Promise -> thực hiện console.log("async2 promise")
sau đó là tới cái setTimeout -> đẩy callback của nó vào Macrotask queue
Tại thời điểm này code sẽ ko chạy tiếp luôn tới console.log("async2 end"), bởi vì như a nói bên trên e đang await Promise, thì Event loop nó sẽ chờ cho Promise resolve.
và tại thời điểm này callstack trống, Microtask queue trống, thì Eventloop sẽ lấy job từ Macrotask queue ra thực hiện -> ta có console.log("async2 setTimeout") và resolve(), ở đây Promise đã resolve
Event loop tiếp tục đưa console.log("async2 end") vào Microtask (vì nó ở sau await). Tại thời điểm này callstack trống, Microtask chỉ có mỗi 1 job vừa đưa vào, vậy nên console.log("async2 end") được lấy ra và thực hiện luôn
cuối cùng là console.log("async1 end") cũng được đưa vào Microtask (vì nó sau await), và hiện tại ngoài microtask ra cũng ko còn gì cả, nên nó cũng sẽ được lấy ra và thực hiện
Tổng hợp lại ta có kết quả như sau:
async1 start
async2 start
async2 promise
async2 setTimeout
async2 end
async1 end
a cho e hỏi 1 sản phẩm quần đùi có 2 attribute là color và size, mỗi color và size lại có nhiều giá trị vậy thiết kế database như nào cho hợp lí ạ để thí dụ cái quần size L màu đỏ là 1 sản phẩm ạ , em cảm ơn
DI là một nguyên tắc về thiết kế, nên nó xuất hiện trong toàn bộ code, khác với Design Pattern, được thiết kế cho một trường hợp riêng biệt. Các Design Pattern hầu hết đều tuân theo DI.
Việc một class triển khai một interface còn có lý do khác đằng sau liên quan đến unit test. Interface cho phép lập trình viên dễ dàng mock các dependency, qua đó cô lập class cần test để viết unit test dễ dàng. Các class tiện tích thì sẽ không triển khai một interface nào (các class tiện ích không có dependency, không nhận dependency làm tham số của hàm, không chứa nghiệp vụ hay use case, là các đoạn code không đổi). Cho nên trong nhiều trường hợp, việc tao interface không thực sự liên quan đến tính thiết kế, mà cho phép mã nguồn nhận những lợi ích khác như testing, hoặc sẵn sàng cho những thay đổi mà chúng ta vốn không biết trước. Việc có một interface đứng sau vẫn đảm bảo việc sửa đổi, mở rộng an toàn và dễ dàng hơn. Nhưng việc luôn tạo interface cũng có thể khiến mã nguồn trông bị rác khi các interface này vô nghĩa, không được sử dụng ở đâu (dù cả trong unit testing).
DI không chỉ giúp đỡ lập trình viên trong trường hợp nêu trên về database.
Đơn giản nhất là Unit testing như giải thích bên trên.
DI cũng giúp các dependency rõ ràng, độc lập hơn, tránh bị tham chiếu vòng tròn A -> B -> C -> A. Khi xây dựng các gói base độc lập sẽ cắt đứt tham chiếu vòng khi mà các dependency đều tham chiếu tới các gói base này chứ không tham chiếu tới nhau, trong khi các gói base không tham chiếu tới gói base khác (hoặc tham chiếu tới gói base cơ sở như là Core, Common, Entities, Domain. Các gói abstract Services sẽ không tham chiếu đến abstract Repositories nhưng cùng tham chiếu đến Core, Common, Entities hoặc Domain, trong khi concrete Services sẽ tham chiếu đến abstract Repositories chứ không phải concrete Repositories).
DI tăng khả năng mở rộng, sửa đổi về sau (những thứ mà không phải lúc nào chúng ta cũng biết trước).
Cho nên, có thể nói lập trình viên luôn được hưởng lợi từ DI.
Đoạn code trên không phải Factory Method mà là Abstract Factory. Mặc dù hai mẫu này tên khá giống nhau nhưng lại có vai trò không liên quan đến nhau.
Abstract Factory che giấu đi sự phức tạp của việc khởi tạo instance cho một họ. Bên tiêu dùng không cần biết hoặc không thể biết cách tạo một instance chứa hành vi mà họ cần, nên họ dựa vào Abstract Factory yêu cầu nó trả về đối tượng theo context được truyền vào.
Factory Method để lại việc khởi tạo đối tượng nhất định cho bên tiêu dùng. Bên tiêu dùng biết cách tạo đối tượng cần thiết theo ngữ cảnh của bên tiêu dùng (kể cả việc gọi lại một Abstract Factory!). Factory Method không nhất thiết trả về các anh em. Nó có thể trả về một instance của class với cấu hình khác nhau. VD:
// base class ghi log vào DB chung
public class LogStorage
{
public virtual void WriteLog(string message)
{
var dbConnection = CreateDbConnection();
WriteLogInternal(message, dbConnection);
}
// Factory Method
public virtual SqlConnection CreateDbConnection()
{
return new SqlConnection(AppSettings.BaseConnection);
}
}
// một dẫn xuất ghi log sang DB khác với DB chung
// Override lại Factory Method để tạo connection tới DB của Sub Sys
public class SubSystemLogStorage : LogStorage
{
public override SqlConnection CreateDbConnection()
{
return new SqlConnection(AppSettings.SubSystemConnection);
}
}
Abstract Factory là một mẫu có độ phổ biến cao, nên tất nhiên chúng ta sẽ gặp nhiều trong thực tế. Mặc dù Dependency Injection làm rất tốt trong việc quản lý đối tượng, và thậm chí còn khiến Singleton biến mất, nhưng Abstract Factory vẫn tồn tại vì nó giúp code trong sáng hơn (mặc dù Abstract Factory hiện tại cũng chỉ gọi lại Dependency Injection container).
Factory Method thì không phổ biến đến thế nhưng cũng không khó gặp. Hãy chú ý đến hành vi của các hàm trong class vì nó sẽ không được đặt tên là FactoryMethod. Có thể có nhiều lần chúng ta khởi tạo một đối tượng tùy theo context, nhưng chưa chắc chúng ta nghĩ ra Factory Method để áp dụng. Có nghĩa bài toán ở đó nhưng được giải theo cách khác.
Cảm ơn bạn nhé. Với Tiếng Việt thì hiện tại có rất nhiều mô hình embedding dành riêng cho Tiếng Việt tùy từng bộ dữ liệu, bạn có thể lên Hugging Face sau đó thử xem mô hình nào phù hợp với bài toán của bạn!
async1 start
async2 start
async2 promise
async2 setTimeout
async2 end
async1 end
Bởi vì sau await thì sẽ bị đưa vào microstask và callback trong settimeout sẻ đưa vào macrotask mà microtask có độ ưu tiên cao hơn macrotask mà tại sao async2 setTimeout lại được in ra trước ạ
Hi, chào @refacore , mình có một chút câu hỏi liên quan đến chủ đề này
Liệu Factory Method Design Pattern có phải là một cách triển khai nguyên lý Dependency Inversion (High-level modules should not depend on the low-level modules, both should depend on the abstraction)
Mình đang hiểu như vậy là đúng bạn nhỉ?
Bạn có góc nhìn nào mới hơn thì mong bạn chia sẻ thêm cho mọi nguời với nhé.
MÌnh đã trải qua 3 dự án mà vẫn chỉ thấy thường một interface có một class triển khai (ngoài trừ thấy ở trong trường hợp dùng ở trong Factory Method Design Pattern ). Không biết trong dự án thực tế bạn đã gặp nhiều trường hợp mà có thể phát huy sức mạnh triệt để của DI hay chưa?, nếu có thì chia sẻ cho lên đây cho mọi người với nhé
Ở trên thì chỉ có những cái callback của Promise (then/catch/finally) mới được đưa vào Microtask queue, còn cái console.log("promise1") bên trong new Promise thì vẫn là code đồng bộ và được thực hiện luôn, do vậy nó chạy trước cả script end
THẢO LUẬN
Hay quá anh ạ! Em chưa bỏ qua bài nào của anh. Mong anh ra bài tiếp theo sớm ạ.
@maitrungduc1410 Em hiểu rồiii, cảm ơn anh ạ.
Bài viết quá tuyệt vời, cảm ơn tác giả
đoạn này e phải chú ý thật kĩ là ở cái
new Promise
e đặtawait
.Khi e đặt
await
trước 1 cái Promise, thì Event loop sẽ chờ cho tới khi cái Promise đó được resolve thì nó mới tiếp tục chạy các phần code bên dưới (nhiều blog khác vẫn gọi là nó "pause execution" - dừng thực hiện chờ cho resolve thì mới "resume").Cụ thể bài của e sẽ như sau:
async1();
được gọi -> thực hiệnconsole.log("async1 start");
await async2()
-> thực hiện console.log("async2 start")await new Promise
-> thực hiệnconsole.log("async2 promise")
setTimeout
-> đẩy callback của nó vào Macrotask queueTại thời điểm này code sẽ ko chạy tiếp luôn tới
console.log("async2 end")
, bởi vì như a nói bên trên e đangawait Promise
, thì Event loop nó sẽ chờ cho Promise resolve.console.log("async2 setTimeout")
vàresolve()
, ở đây Promise đã resolveconsole.log("async2 end")
vào Microtask (vì nó ở sauawait
). Tại thời điểm này callstack trống, Microtask chỉ có mỗi 1 job vừa đưa vào, vậy nênconsole.log("async2 end")
được lấy ra và thực hiện luônconsole.log("async1 end")
cũng được đưa vào Microtask (vì nó sauawait
), và hiện tại ngoài microtask ra cũng ko còn gì cả, nên nó cũng sẽ được lấy ra và thực hiệnTổng hợp lại ta có kết quả như sau:
hay ạ
Bài viết hay ngắn gọn, đầy đủ. Nhưng ko biết các update này ở Spring security 6 có lợi ích gì lớn ko ạ ?
a cho e hỏi 1 sản phẩm quần đùi có 2 attribute là color và size, mỗi color và size lại có nhiều giá trị vậy thiết kế database như nào cho hợp lí ạ để thí dụ cái quần size L màu đỏ là 1 sản phẩm ạ , em cảm ơn
👍️👍️
DI là một nguyên tắc về thiết kế, nên nó xuất hiện trong toàn bộ code, khác với Design Pattern, được thiết kế cho một trường hợp riêng biệt. Các Design Pattern hầu hết đều tuân theo DI.
Việc một class triển khai một interface còn có lý do khác đằng sau liên quan đến unit test. Interface cho phép lập trình viên dễ dàng mock các dependency, qua đó cô lập class cần test để viết unit test dễ dàng. Các class tiện tích thì sẽ không triển khai một interface nào (các class tiện ích không có dependency, không nhận dependency làm tham số của hàm, không chứa nghiệp vụ hay use case, là các đoạn code không đổi). Cho nên trong nhiều trường hợp, việc tao interface không thực sự liên quan đến tính thiết kế, mà cho phép mã nguồn nhận những lợi ích khác như testing, hoặc sẵn sàng cho những thay đổi mà chúng ta vốn không biết trước. Việc có một interface đứng sau vẫn đảm bảo việc sửa đổi, mở rộng an toàn và dễ dàng hơn. Nhưng việc luôn tạo interface cũng có thể khiến mã nguồn trông bị rác khi các interface này vô nghĩa, không được sử dụng ở đâu (dù cả trong unit testing).
DI không chỉ giúp đỡ lập trình viên trong trường hợp nêu trên về database.
Cho nên, có thể nói lập trình viên luôn được hưởng lợi từ DI.
Đoạn code trên không phải Factory Method mà là Abstract Factory. Mặc dù hai mẫu này tên khá giống nhau nhưng lại có vai trò không liên quan đến nhau.Abstract Factory che giấu đi sự phức tạp của việc khởi tạo instance cho một họ. Bên tiêu dùng không cần biết hoặc không thể biết cách tạo một instance chứa hành vi mà họ cần, nên họ dựa vào Abstract Factory yêu cầu nó trả về đối tượng theo context được truyền vào.Factory Method để lại việc khởi tạo đối tượng nhất định cho bên tiêu dùng. Bên tiêu dùng biết cách tạo đối tượng cần thiết theo ngữ cảnh của bên tiêu dùng (kể cả việc gọi lại một Abstract Factory!). Factory Method không nhất thiết trả về các anh em. Nó có thể trả về một instance của class với cấu hình khác nhau. VD:Abstract Factory là một mẫu có độ phổ biến cao, nên tất nhiên chúng ta sẽ gặp nhiều trong thực tế. Mặc dù Dependency Injection làm rất tốt trong việc quản lý đối tượng, và thậm chí còn khiến Singleton biến mất, nhưng Abstract Factory vẫn tồn tại vì nó giúp code trong sáng hơn (mặc dù Abstract Factory hiện tại cũng chỉ gọi lại Dependency Injection container).Factory Method thì không phổ biến đến thế nhưng cũng không khó gặp. Hãy chú ý đến hành vi của các hàm trong class vì nó sẽ không được đặt tên là FactoryMethod. Có thể có nhiều lần chúng ta khởi tạo một đối tượng tùy theo context, nhưng chưa chắc chúng ta nghĩ ra Factory Method để áp dụng. Có nghĩa bài toán ở đó nhưng được giải theo cách khác.Cảm ơn bạn nhé. Với Tiếng Việt thì hiện tại có rất nhiều mô hình embedding dành riêng cho Tiếng Việt tùy từng bộ dữ liệu, bạn có thể lên Hugging Face sau đó thử xem mô hình nào phù hợp với bài toán của bạn!
Anh có thể giải thích cho em kết quả của đoạn code này không ạ?
Kết quả là:
Bởi vì sau await thì sẽ bị đưa vào microstask và callback trong settimeout sẻ đưa vào macrotask mà microtask có độ ưu tiên cao hơn macrotask mà tại sao async2 setTimeout lại được in ra trước ạ
hay quá anh ơi, e phải đăng ký để commnet khen a )))
Bài viết hay! Cảm ơn bạn
Cảm ơn bạn, bài viêt chi tiết quá.
Hi, chào @refacore , mình có một chút câu hỏi liên quan đến chủ đề này
Liệu Factory Method Design Pattern có phải là một cách triển khai nguyên lý Dependency Inversion (High-level modules should not depend on the low-level modules, both should depend on the abstraction)
Interface Database { String connectToDataBase() ; }
MongoDb implement Database { String connectToDataBase(){
return "this is MongoDb"; } }
Mysql implement Database { String connectToDataBase(){
return "this is Mysql "; } }
// trên đây chúng ta cũng có thể thấy MongoDb và Mysql (High-level đang phụ thuộc vào thằng Interface Database (abstraction) )
Class DatabaseFactory {
public Database createConnection(string type) {
if(type.equa("MongoDb")){ return new MongoDb ();
} else if(type.equa("Mysql")){ return new Mysql(); }
}
// nếu mình muốn có một loại database khác thì cũng chỉ việc tạo một loại database khác và extends Database và else if(type.equa("***")){return....
Cám ơn bạn nhiều
Cảm ơn a, mong anh ra nhiều bài viết nữa
Cảm ơn bạn
👍️👍️👍️
👍️
e xem những ví dụ trước của a nói nhé, ví dụ
Ở trên thì chỉ có những cái callback của Promise (then/catch/finally) mới được đưa vào Microtask queue, còn cái
console.log("promise1")
bên trongnew Promise
thì vẫn là code đồng bộ và được thực hiện luôn, do vậy nó chạy trước cảscript end