LẬP TRÌNH KHÔNG ĐỒNG BỘ TRONG JS: Từ Callback hell cho đến Async / Await

Ngày đăng: 07/11/2020   -    Cập nhật: 07/11/2020
Một trong những chìa khóa để lập trình web thành công là có thể thực hiện hàng chục lệnh gọi AJAX trên mỗi trang.


Đây là một thách thức lập trình không đồng bộ điển hình và phần lớn giải pháp bạn chọn đối phó với các lệnh gọi không đồng bộ (asynchronous) sẽ làm hỏng trang web của bạn.



"Đồng bộ hóa các tác vụ không đồng bộ trong JavaScript là một vấn đề nghiêm trọng trong một khoảng thời gian dài"


Vần đề này tạo ra rất nhiều khó khăn cho các các lập trình viên front end và các lập trình viên back-end sử dụng Node.js.


Nhưng lập trình không đồng bộ là một phần công việc hàng ngày của chúng ta vì thế, cho dù bạn muốn tránh cũng không được.



Lịch sử về lập trình không đồng bộ trong JavaScript



Lập trình không đồng bộ trong JavaScript


Để thực hiện nhiệm vụ lập trình không đồng bộ trong JavaScript, giải pháp đầu tiên và đơn giản nhất là sử dụng các hàm lồng nhau dưới dạng các lệnh Callback.


Tuy nhiên, giải pháp này đã dẫn đến một thứ được gọi là callback hell, và nó có quá nhiều vấn đề.


Sau đó, trong ES6 chúng ta có Promises.


Pattern này giúp code dễ đọc hơn rất nhiều, nhưng nó khác xa với nguyên tắc Không lặp lại chính mình (DRY - Don't Repeat Yourselft).


Vẫn có quá nhiều trường hợp bạn phải lặp lại các đoạn code giống nhau để quản lý đúng luồng ứng dụng.


Và bổ sung mới nhất cho vấn đề này đã được phát hành dưới dạng câu lệnh async / await ở phiên bản ES7.


Cuối cùng có cách lập trình không đồng bộ trong JavaScript dễ đọc và dễ viết như bất kỳ đoạn code nào khác.


Bên dưới đây, hãy cùng xem các ví dụ về từng giải pháp từ callback hell đến async / await để thấy rõ sự phát triển của lập trình không đồng bộ trong JavaScript.


Để làm điều này, chúng ta sẽ kiểm tra một tác vụ đơn giản thực hiện các bước sau:



  • Xác minh tên người dùng và mật khẩu của người dùng.
  • Lấy quyền cho người dùng.
  • Ghi nhật ký thời gian truy cập ứng dụng cho người dùng.


Cách #1: Sử dụng Callback hell (Kim tự thám của sự diệt vong)



Giải pháp cổ lỗ để đồng bộ hóa các cuộc gọi này là thông qua các cuộc gọi lại lồng nhau.


Đây là một cách làm phù hợp cho các tác vụ không đồng bộ đơn giản, nhưng sẽ không mở rộng quy mô được do vấn đề được gọi là callback hell.



Minh họa Callback hell trong JavaScript


Code cho ba tác vụ đơn giản trên sẽ trông giống như sau:



const verifyUser = function(usernamepasswordcallback) {
    dataBase.verifyUser(usernamepassword, (erroruserInfo=> {
        if (error) {
            callback(error)
        } else {
            dataBase.getRoles(username, (errorroles=> {
                if (error) {
                    callback(error)
                } else {
                    dataBase.logAccess(username, (error=> {
                        if (error) {
                            callback(error);
                        } else {
                            callback(nulluserInforoles);
                        }
                    })
                }
            })
        }
    })
};
 


Mỗi hàm nhận một đối số là một hàm khác được gọi với một tham số là phản hồi của hành động trước đó.


Có rất nhiều người sẽ thiếu máu lên não, hoa mắt chóng mặt khi đọc chương trình trên.


Nếu một trang web có hàng trăm đoạn code tương tự thế này thì đến chính người viết ra cũng chịu chết khi maintain.


Ví dụ này thậm chí còn phức tạp hơn khi database.getRoles là một hàm khác có các lệnh gọi lại lồng nhau.




const getRoles = function(usernamecallback) {
    database.connect((connection=> {
        connection.query('get roles sql', (result=> {
            callback(nullresult);
        })
    });
};
 


Ngoài việc code khó bảo trì, nguyên tắc DRY cũng hoàn toàn không có giá trị trong trường hợp này.


Ví dụ: Xử lý lỗi được lặp lại trong mỗi hàm và lệnh callback được gọi từ mỗi hàm lồng nhau.


Đối với các tác vụ không đồng bộ phức tạp hơn, chẳng hạn như lặp qua các lệnh gọi không đồng bộ, nó lại càng khó hơn.


Trên thực tế, không có cách đơn giản nào để hoàn thành nhiệm vụ này nếu bạn sử dụng callback.


Đây là lý do tại sao các thư viện JavaScript Promise như BluebirdQ lại nhận được rất nhiều sự quan tâm.


Chúng cung cấp một cách để thực hiện các tác vụ không đồng bộ phổ biến mà bản thân ngôn ngữ đó chưa cung cấp (Hoặc phiên bản đó chưa có).


Đó là lúc những Promises xuất hiện chính thức trong phiên bản ES6.


* Promises được đề xuất từ năm 2012 (khái niệm này còn có từ 1976 cơ). Nhưng mãi đến 2015 nó mới chính thức được ECMAScript chấp thuận.



Cách #2: Sử dụng JavaScript Promises



Promises là bước tiến tiếp theo trong việc tìm cách thoát khỏi callback hell.


Phương pháp này không loại bỏ việc sử dụng các lệnh callback, nhưng nó làm cho chuỗi các hàm trở nên dễ đọc và dễ viết hơn nhiều.


Ảnh


Với Promises, code trong ví dụ JavaScript không đồng bộ của chúng ta sẽ trông giống như sau:




const verifyUser = function(usernamepassword) {
    database.verifyUser(usernamepassword)
        .then(userInfo => dataBase.getRoles(userInfo))
        .then(rolesInfo => dataBase.logAccess(rolesInfo))
        .then(finalResult => {
            // Làm gì đó mà call back sẽ làm
        })
        .catch((err=> {
            // Xử lý lỗi
        });
};
 


Để đạt được kiểu đơn giản này, tất cả các hàm được sử dụng trong ví dụ sẽ phải được Promisised.


Hãy cùng xem phương thức getRoles sẽ được cập nhật như thế nào để trả về Promise:




const getRoles = function(username) {
    return new Promise((resolvereject=> {
        database.connect((connection=> {
            connection.query('get roles sql', (result=> {
                resolve(result);
            })
        });
    });
};
 


Chúng ta đã sửa đổi phương thức để trả về một Promise, với hai lệnh callback và bản thân Promise thực hiện các hành động từ phương thức.


Bây giờ, giải quyết và từ chối các lệnh callback sẽ được ánh xạ tới các phương thức Promise.thenPromise.catch tương ứng.


Bạn có thể nhận thấy rằng phương thức getRoles vẫn có xu hướng giống callback hell.


Điều này là do cách các phương thức cơ sở dữ liệu được tạo ra do chúng không trả về Promise.


Nếu các phương thức truy cập cơ sở dữ liệu của chúng ta cũng trả về Promise thì phương thức getRoles sẽ giống như sau:




const getRoles = new function(userInfo) {
    return new Promise((resolvereject=> {
        database.connect()
            .then((connection=> connection.query('get roles sql'))
            .then((result=> resolve(result))
            .catch(reject)
    });
};
 


Cách #3: Sử dụng Async / Await



Vấn đề callback hell đã được giảm thiểu đáng kể với sự ra đời của Promises. Tuy nhiên, chúng ta vẫn phải dựa vào các lệnh callback được chuyển cho các phương thức thencatch của Promise.


Và Promises đã mở đường cho một trong những cải tiến thú vị nhất trong JavaScript.


ECMAScript 2017 đã mang lại Promise cải tiến dưới dạng câu lệnh asyncawait.


Chúng cho phép chúng ta viết code dựa trên Promise như thể nó đồng bộ, nhưng không chặn luồng chính.




const verifyUser = async function(usernamepassword) {
    try {
        const userInfo = await dataBase.verifyUser(usernamepassword);
        const rolesInfo = await dataBase.getRoles(userInfo);
        const logStatus = await dataBase.logAccess(userInfo);
        return userInfo;
    } catch (e) {
        // Xư lý lỗi nếu cần
    }
};
 


Chờ Promise giải quyết chỉ được phép trong các hàm async, có nghĩa là verifyUser phải được xác định bằng cách sử dụng async function.


Tuy nhiên, khi thay đổi nhỏ này được thực hiện, bạn có thể await bất kỳ Pomises nào mà không cần thay đổi bổ sung trong các phương thức khác.



Async - Một lời hứa đã được mong đợi từ lâu



Các hàm không đồng bộ là bước hợp lý tiếp theo trong sự phát triển của lập trình không đồng bộ trong JavaScript. Chúng sẽ làm cho code của bạn sạch hơn và dễ bảo trì hơn nhiều.


Việc khai báo một hàm là async sẽ đảm bảo rằng nó luôn trả về một Promises


Tại sao bạn nên bắt đầu sử dụng hàm async trong JavaScript ngay hôm nay?



  • Code sạch hơn nhiều.
  • Xử lý lỗi đơn giản hơn nhiều và nó dựa vào try / catch giống như trong bất kỳ code đồng bộ nào khác.
  • Gỡ lỗi đơn giản hơn nhiều. Đặt điểm ngắt bên trong khối .then sẽ không chuyển sang khối .then tiếp theo vì nó chỉ đi qua code đồng bộ. Tuy nhiên, bạn có thể bước qua các cuộc gọi await như thể chúng là các cuộc gọi đồng bộ.


---
HỌC VIỆN ĐÀO TẠO CNTT NIIT - ICT HÀ NỘI
Học Lập trình chất lượng cao (Since 2002). Học làm Lập trình viên. Hành động ngay!
Đc: Tầng 3, 25T2, N05, Nguyễn Thị Thập, Cầu Giấy, Hà Nội
SĐT: 02435574074 - 0914939543
Email: hello@niithanoi.edu.vn
Fanpage: https://facebook.com/NIIT.ICT/
 
#niit #niithanoi #niiticthanoi #hoclaptrinh #khoahoclaptrinh #hoclaptrinhjava #hoclaptrinhphp #java #php #python
Bình luận Facebook
Khóa học liên quan đến bài viết

Khóa học PHP Full stack [2023] cho người mới bắt đầu

96 giờ
Khóa học Lập trình PHP Full stack, phiên bản cập nhật lần thứ 8. Dạy Lập trình PHP bài bản từ Front end đến Back end + Laravel. Hướng dẫn làm 2 Dự Án Web lớn

KHÓA HỌC PYTHON HƯỚNG ĐỐI TƯỢNG

50 giờ
Khóa học giúp học viên sử dụng thành thạo ngôn ngữ Lập trình Python (3x). Hiểu và phát triển được Ứng dụng Web với Django Framework. Học thực hành với Giảng viên cao cấp.

Lập trình PHP với Laravel Framework

42 giờ
Khóa học Lập trình PHP với Laravel Framework được NIIT - ICT HÀ NỘI xây dựng nhằm hoàn thiện kỹ năng lập trình web các các bạn đã biết Lập trình Web PHP thuần.

Khóa học Java Full stack (IJFD)

104 giờ
Học lập trình Java Fullstack với khóa học được xây dựng theo lộ trình bài bản, từ JAVA CƠ BẢN đến JAVA WEB và nâng cao về JAVA FRAMEWORK như: Spring Boot, Hibernate
Mục lục
Đăng ký tư vấn
Nhân viên gọi điện tư vấn miễn phí sau khi đăng ký
Được cập nhật các ưu đãi sớm nhất
Hotline: 0383180086
Tên không được để trống
Số điện thoại không được để trống
Email không được để trống
Hãy đăng ký để nhận những thông tin mới nhất về học bổng mới nhất tại NIIT - ICT Hà Nội
top
Đóng lại Đăng ký học tại NIIT - ICT Hà Nội
6260+ học viên đã theo học tại NIIT - ICT Hà Nội và có việc làm tốt trong ngành lập trình. Nắm lấy cơ hội ngay hôm nay!
Chọn khóa học
  • KHÓA HỌC LẬP TRÌNH FRONT END VỚI REACT.JS
  • KHÓA HỌC LẬP TRÌNH PHP WEB
  • Khóa học PHP Full stack [2023] cho người mới bắt đầu
  • Khóa học BIG DATA với Hadoop và Spark
  • Khóa học Lập trình Android tại Hà Nội
  • [Tuyển sinh 2023] Lập trình viên Quốc tế DigiNxt
  • Khóa học Tiền lương & Phúc lợi (C&B Excel) tại Hà Nội
  • LẬP TRÌNH GAME
    • Khóa học Lập trình Game Unity
  • LẬP TRÌNH WEB FRONT END
    • KHÓA HỌC PYTHON HƯỚNG ĐỐI TƯỢNG
    • KHÓA HỌC ANGULAR & TYPESCRIPT (FRONT END)
  • LẬP TRÌNH WEB BACK END
    • LẬP TRÌNH JAVA WEB VỚI FRAME WORK
    • Lập trình Web với Django
    • Lập trình PHP với Laravel Framework
  • CHƯƠNG TRÌNH ĐÀO TẠO ỨNG DỤNG CÔNG NGHỆ
    • Khóa học Tiền lương & Phúc lợi (C&B Excel) tại TP HCM
  • LẬP TRÌNH WEB FULL STACK
    • Khóa học Java Full stack (IJFD)
  • LẬP TRÌNH MOBILE
    • FRONT-END VỚI REACTJS VÀ REACT NATIVE
    • Lập trình Android Nâng cao
  • ĐÀO TẠO CHO DOANH NGHIỆP
    • KHÓA HỌC BUSINESS ANALYSIC TỪ CƠ BẢN ĐẾN NÂNG CAO 2023
    • Khóa học Magento: Làm chủ CMS TMĐT lớn nhất
    • Khóa học IOT: Xây dựng Sản phẩm IOT với Raspberry Pi
    • Khóa học Automation Testing Chuyên nghiệp
  • KHÓA HỌC DỰ ÁN
    • Học sử dụng bộ Office: Word, Excel, Power Point, Mail chuyên nghiệp
  • KHÓA HỌC KHÁC
    • VBA Excel Toàn Tập (Cơ Bản - Nâng Cao)
    • VBA Excel Nâng cao
    • Khóa học JMeter: Performance Testing
    • Khóa học Tester đạt chuẩn Quốc tế ISTQB Foundation Level
    • Khoá Học Tester đạt chuẩn quốc tế ISTQB Advanced Level
Bạn chưa chọn khóa học cần đăng ký
Tên không được để trống
Số điện thoại không được để trống
Email không được để trống
Đăng ký học thành công!
Cảm ơn bạn đã đăng ký học tại NIIT - ICT HÀ NỘI!