Xin chào mọi người, hôm nay mình sẽ giới thiệu về Callback, Promise, Async/Await trong JS, mong mọi người theo dõi
Trước khi tìm hiểu về Callback, Promise, Async/Await trong Javascript thì chúng ta sẽ đi qua một ví dụ sau:
Giả sử bạn cần xin số điện thoại của một người mình tạm gọi là bạn ABC từ người bạn của mình là bạn XYZ
Ví dụ
constxinSdt=()=>{let sdt;
console.log('1 - Gọi điện cho thằnng bạn XYZ, cho tao xin số của thằng ABC');
console.log('2 - XYZ đang tìm số của ABC cho bạn');// Giả sử thời gian tìm số điện thoại của ABC mất khoảng 1ssetTimeout(()=>{
sdt =123456789;
console.log('3 - XYZ đã tìm thấy số của ABC');},1000);
console.log(`4 - Số điện thoại của ABC là ${sdt}`);};// Gọi hàm để xem kết quảxinSdt();
Kết quả:
Như mọi người thấy thì trước thì thứ tự log ra nó đang là 1243 chứ không phải 1234, vì sao lại như vậy, là vì Javascript là một ngôn như đơn luồng và bất đồng bộ, như ở trong ví dụ trên thì trong code mình có setTimeout cho một đoạn code thì khi hàm xinSdt được thực thi thì nó sẽ không đợi phần mình setTimeout được chạy xong rồi nó mới chạy tiếp mà nó sẽ chạy xuống dưới luôn dẫn tới việc kết quả hiển thị ra là 1243, trong thực tế khi làm việc với JS thì sẽ rất nhiều lúc xẩy ra trường hợp này, ví dụ như khi bạn gọi một API nào đó thì do trong quá trình gọi sẽ mất một khoảng thời gian nào đó để server trả kết quả về thì lúc đó cũng xẩy ra trong hợp tương tự như trên
Để giải quyết được vấn đề này trên cho kết quả hiển thị về 1234 thì ta có thể dùng Callback, Promise, Async/Await
1) Callback
Callback function có thể được hiểu như sau: khi ta truyền một đoạn code (Hàm A) này vào một đoạn code khác (Hàm B). Tới một thời điểm nào đó, Hàm A sẽ được hàm B gọi lại (callback). Lý thuyết là vậy giờ chúng ta cùng đi vào ví dụ nhé
Ví dụ 1
Mình sẽ áp dụng callback vào ví dụ ở trên xem thử nó có như mong muốn của chúng ta không nhé
constxinSdt=(callback)=>{let sdt;
console.log('1 - Gọi điện cho thằnng bạn XYZ, cho tao xin số của thằng ABC');
console.log('2 - XYZ đang tìm số của ABC cho bạn');// Giả sử thời gian tìm số điện thoại của ABC mất khoảng 1ssetTimeout(()=>{
sdt =123456789;
console.log('3 - XYZ đã tìm thấy số của ABC');callback(sdt);},1000);};consthienThiSdtSauKhiNhan=sdt=> console.log(`4 - Số điện thoại của ABC là ${sdt}`);// Gọi hàm để xem kết quảxinSdt(hienThiSdtSauKhiNhan);
Kết quả:
Giờ thì nó đã hiển thị ra đúng như ta mong muốn là 1234
Trong đoạn code trên thì mình có tách phần hiển thị số điện thoại khi nhận được ra một hàm riêng sau đó mình truyền nó là một tham số của hàm xinSdt thì sau khi mà XYZ tìm được số điện thoại xong thì mới gọi đến hàm hienThiSdtSauKhiNhan thì mình sẽ không bị hiển thị sai kết quả nữa
Nếu mà Callback có thể xử lý được bất đồng bộ rồi thì người ta còn sinh ra Promise với Async/Await làm gì nữa, thì chúng ta sẽ đi vào ví dụ sau:
Ví dụ 2
Giả sử khi XYZ tìm được số điện thoại rồi nhưng mà điện thoại lại hết pin không thể báo lại được cho bạn nữa mà cần phải sạc pin xong mới gọi báo lại được cho mình
constxinSdt=(sacPin)=>{let sdt;
console.log('Gọi điện cho thằnng bạn XYZ, cho tao xin số của thằng ABC');
console.log('XYZ đang tìm số của ABC cho bạn');// Giả sử thời gian tìm số điện thoại của ABC mất khoảng 1ssetTimeout(()=>{
sdt =123456789;
console.log('XYZ đã tìm thấy số của ABC')sacPin(sdt, hienThiSdtSauKhiNhan);},1000);}constsacPin=(sdt, hienThiSdtSauKhiNhan)=>{
console.log('Đang sạc Pin');setTimeout(()=>{
console.log('Pin đầy, XYZ bắt đầu gọi điện thoại cho bạn để báo số điện thoại');hienThiSdtSauKhiNhan(sdt);},2000);}consthienThiSdtSauKhiNhan=(sdt)=>{
console.log(`Đã nhận được số điện thoại của ABC là ${sdt}`);}xinSdt(sacPin);
Kết quả:
Trong ví dụ trên thì khi mà XYZ daxdd tìm được số điện thoại của ABC rồi định gọi điện cho bạn nhưng phát sinh hành động là điện thoại hết pin không thể gọi cho bạn, mà cần phải sạc pin xong mới gọi được cho bạn, giả sử trong quá trình gọi cho bạn mà lại phát sinh thêm các vấn đề khác thì lúc này code sẽ sẽ khá là rối và khó kiểm soát được trong trường hợp này người ta gọi là callback hell. Để code được nhìn dễ dàng hơn thì người ta đã snh ra Promise, vậy chúng ta cùng qua Promise xem xử lý thế nào nhé
2) Promise
- Promise là một cơ chế trong JavaScript giúp bạn thực thi các tác vụ bất đồng bộ mà không rơi vào callback hell hay pyramid of doom, là tình trạng các hàm callback lồng vào nhau ở quá nhiều tầng.
Cú pháp
const promise =newPromise(function(resolve, reject){// Code here});
Trong đó:
-
Hàm được truyền vào new Promise gọi là executor.
-
Ban đầu, Promise có state là pending và kết quả value là undefined. Khi executor kết thúc công việc, nó sẽ gọi đến 1 trong 2 hàm được truyền vào:
-
resolve(value): để xác định rằng công việc đã thực hiện thành công
- state chuyển thành fulfilled
- kết quả là value
-
reject(error): để xác định rằng đã có lỗi xảy ra
- state chuyển thành rejected
- kết quả là error
-
Giờ chúng ta sẽ sử dụng Promise để xử lý bài toán ở trên xem thế nào nhé
Ví dụ
constxinSdt=()=>{
console.log('Gọi điện cho thằnng bạn XYZ, cho tao xin số của thằng ABC');
console.log('XYZ đang tìm số của ABC cho bạn');// Giả sử thời gian tìm số điện thoại của ABC mất khoảng 1s// phần tìm kiếm này mình sẽ sử dụng Promiseconst thaoTacTimKiem =newPromise((resolve, reject)=>{// Giả sử là tìm thấy số điện thoại của ABClet daTimThaySdt =true;setTimeout(()=>{if(daTimThaySdt){
sdt =123456789;
console.log(`XYZ đã tìm thấy số của ABC`);resolve(sdt);}else{reject('XYZ đã tìm và không có số điện thoại của ABC')}},1000);});return thaoTacTimKiem;}// Sac PinconstsacPin=(sdt)=>{
console.log('Đang sạc Pin');// Giả sử sạc pin không bị hỏnglet sacPinBiHong =false;// Phần sạc pin này mình sẽ sử dụng Promiseconst thaoTacSacPin =newPromise((resolve, reject)=>{setTimeout(()=>{if(!sacPinBiHong){// Pin đã được sạc đầy
console.log('Pin đầy, XYZ bắt đầu gọi điện thoại cho bạn để báo số điện thoại');resolve(sdt);}else{// Sạc pin bị hỏngreject('Sạc Pin bị hỏng rồi');}},2000);});return thaoTacSacPin;}consthienThiSdtSauKhiNhan=(sdt)=> console.log(`Đã nhận được số điện thoại của ABC là ${sdt}`);// Gọi hàm để xem kết quảxinSdt().then(sdt=>{returnsacPin(sdt);}).then((sdt)=>hienThiSdtSauKhiNhan(sdt)).catch(error=>{
console.log(error);});
Kết quả:
Khi áp dụng Promise vào thì ta thấy code sẽ dễ theo dõi và xử lý hơn
3) Async/await
- Được giới thiệu trong ES8
- Async/await là một cú pháp đặc biệt giúp bạn làm việc với Promise dễ dàng hơn. Khi sử dụng async/await, cấu trúc chương trình xử lý bất đồng bộ sẽ giống với chương trình xử lý đồng bộ hơn.
Giờ chúng ta sẽ sử dụng Async/await để xử lý bài toán trên xem thế nào nhé
Ví dụ
constxinSdt=()=>{
console.log('Gọi điện cho thằnng bạn XYZ, cho tao xin số của thằng ABC');
console.log('XYZ đang tìm số của ABC cho bạn');// Giả sử thời gian tìm số điện thoại của ABC mất khoảng 1s// phần tìm kiếm này mình sẽ sử dụng Promiseconst thaoTacTimKiem =newPromise((resolve, reject)=>{// Giả sử là tìm thấy số điện thoại của ABClet daTimThaySdt =true;setTimeout(()=>{if(daTimThaySdt){
sdt =123456789;
console.log(`XYZ đã tìm thấy số của ABC`);resolve(sdt);}else{reject('XYZ đã tìm và không có số điện thoại của ABC')}},1000);});return thaoTacTimKiem;}// Sac PinconstsacPin=()=>{
console.log('Đang sạc Pin');// Giả sử sạc pin không bị hỏnglet sacPinBiHong =false;// Phần sạc pin này mình sẽ sử dụng Promiseconst thaoTacSacPin =newPromise((resolve, reject)=>{setTimeout(()=>{if(!sacPinBiHong){// Pin đã được sạc đầy
console.log('Pin đầy, XYZ bắt đầu gọi điện thoại cho bạn để báo số điện thoại');resolve(sdt);}else{// Sạc pin bị hỏngreject('Sạc Pin bị hỏng rồi');}},2000);});return thaoTacSacPin;}consthienThiSdtSauKhiNhan=(sdt)=> console.log(`Đã nhận được số điện thoại của ABC là ${sdt}`);// Gọi hàm để hiển thị kết quảconsthanhDong=async()=>{try{const sdt =awaitxinSdt();awaitsacPin(sdt);hienThiSdtSauKhiNhan(sdt);}catch(error){
console.log(error);}}hanhDong();
Kết quả:
Nhìn vào logic ở trên ta thấy nó rõ ràng và mạch lạc hơn một chút so với Promise
Lời kết
Trên đây là một số kiến thức cơ bản về xử lý bất đồng bộ trong JavaScript với callback, promise và async/await chứ chưa đầy đủ về Promise hay async/await mà chỉ giúp bạn thấy được cách sử dụng cơ bản, cũng như ưu điểm của chúng so với việc sử dụng callback thông thường. các bạn có thể tiềm hiểu chi tiết hơn ở phía dưới. Cám ơn các bạn đã theo dõi
Nguồn tham khảo
Nguồn: viblo.asia