1. Synchronous trong JavaScript là gì?

Synchronous – đồng bộ / tuần tự. Tức là code chương trình sẽ chạy tuần tự từ trên xuống dưới. Khi nào lệnh trên hoàn thành thì lệnh dưới mới được chạy. Đây là cách viết code rất thường dùng. Cái hay của nó là dễ kiểm soát quá trình xử lý.

Việc này rất hữu ích cho việc sử lý lỗi. Do đó, lập trình đồng bộ dễ dàng kiểm soát quá trình xử lí. Nhưng một nhược điểm của nó là việc sinh ra trạng thái chờ. Nếu lệnh trên chạy quá lâu thì làm lãng phí thời gian của cách lệnh dưới. Lúc này nó gây ra cảm giác giật lag cho người dùng vì họ không thể thực hiện thao tác khác nếu thao tác trước đó chưa được xử lí xong.

Ví dụ:

<script>
    document.write("Thành Nguyễn");
    document.write("Laptrinhtudau.com");
    /*Kết quả:
         Thành Nguyễn
         Laptrinhtudau.com
    */
</script>
<script>
 // ...
 thuchienNauCom();
 thuchienChienTrung();
 thuchienAnCom(); 
</script>

Code chạy lần lượt từ trên xuống, lệnh trên chạy dù có lâu thì chỉ chỉ khi thực hiện xong, lệnh dưới mới chạy.

2. Asynchronous trong JavaScript

Asynchronous bất đồng bộ/bất tuần tự. Tức là code chương trình không hẳn tuần tự nữa. Nhiều lệnh có thể thực hiện cùng lúc. Có khi lệnh dưới cho kết thúc và cho kết quả trước cả lệnh phía trên.

Để giải quyết vấn đề của quá trình đồng bộ thì chúng ta sẽ sử dụng quá trình bất đồng bộ này. Ví dụ như việc chạy đồng thời 100 câu lệnh đọc file cùng một lúc => Chúng ta sẽ chỉ mất khoảng 0.5s đến 1s thay vì 50s như quá trình đồng bộ(đọc mỗi file cần 0.5s).

Điều này đã tối ưu được sức mạnh của hệ thống, thứ hai giúp giảm thời gian chờ, giúp code chạy nhanh hơn. Nhưng nó cũng có những nhược điểm của riêng mình. Không phải hệ thống nào cũng dùng bất đồng bộ được, thứ hai là khó làm quen xử lý và kiểm soát lỗi phát sinh vì nó không theo thứ tự.

Trong Js, các hàm setTimeout , setInterval , fetch … là tiêu biểu cho các hàm xử lý bất đồng bộ trong Js.

Ví dụ:

<script>
function myDisplayer(something) {
  document.write(something);
}

function myCalculator(num1, num2, myCallback) {
  let sum = num1 + num2;
  myCallback(sum);
}

myCalculator(5, 5, myDisplayer);
</script>

Trong ví dụ trên, myDisplayerlà tên của một hàm. Nó được chuyển đến myCalculator() như một đối số. Trong thực tế, callback thường được sử dụng với các hàm không đồng bộ. Một ví dụ điển hình là Js là setTimeout() .

3. Hàm setTimeout() và setInterval() trong JavaScript

3.1. Hàm setTimeout()

SetTimeout() thực thi một block code sau thời gian được chỉ định. Phương thức này chỉ thực thi code một lần. Cú pháp thường được sử dụng của Js setTimeout là: setTimeout(hamCanThucThi, miliGiay);

  • hamCanThucThi – một hàm chứa một tác vụ nào đó cần được thực thi
  • miliGiay – thời gian mà sau đó hàm hamCanThucThi được thực thi

Khi sử dụng hàm setTimeout() , bạn có thể chỉ định một hàm gọi lại được thực thi khi hết thời gian:

<script>
setTimeout(myFunction, 3000);

function myFunction() {
  document.write("Lập trình từ đầu");
}
</script>

myFunction được sử dụng như một callback . Nó cũng được chuyển đến setTimeout() như một đối số. 3000 là số mili giây trước khi hết thời gian, vì vậy myFunction() sẽ được gọi sau 3 giây.

Lưu ý

Khi bạn truyền một hàm làm đối số, hãy nhớ không sử dụng dấu ngoặc đơn.

  • Đúng : setTimeout (myFunction, 3000);
  • Sai : setTimeout (myFunction (), 3000);

Thay vì chuyển tên của một hàm làm đối số cho một hàm khác, ta luôn có thể chuyển toàn bộ một hàm để thay thế:

<script>
setTimeout(function() { myFunction("Lập trình từ đầu"); }, 3000);

function myFunction(value) {
  document.write(value);
}
</script>

3.2. Hàm setInterval()

Khi sử dụng hàm setInterval() , ta có thể chỉ định một hàm gọi lại sẽ được thực thi cho mỗi khoảng thời gian

<h1 id="demo"></h1>

<script>
setInterval(myFunction, 1000);

function myFunction() {
  let d = new Date();
  document.getElementById("demo").innerHTML=
  d.getHours() + ":" +
  d.getMinutes() + ":" +
  d.getSeconds();
}
</script>

4. Xử lý bất đồng bộ bằng Callback

Hàm callback là một hàm được truyền vào như tham số cho 1 hàm khác. Rất nhiều hàm trong Js sử dụng callback cho việc xử lý. Ví dụ:

function asyncFunction(callback) {
   document.write("Start");
   setTimeout(() => {
      callback();
   }, 1000);
   document.write("Waiting");
}

let printEnd = function() {
   document.write("End");
}

asyncFunction(printEnd)

Ở đây đã dùng setTimeout để giả sử cho thời gian chờ là 1s.

Tất nhiên callback cũng có nhược điểm của nó. Nếu như ta cần thực hiện nhiều câu lệnh bất đồng bộ thì cần phải lồng từng đó callback với nhau, khiến cho code sẽ vô cùng khó đọc, khó debug cũng như phát triển (trường hợp này được gọi là Callback Hell).

Ví dụ:

function getData(link, callback) {
   setTimeout(() => {
      callback();
   }, 1000)
}

getData("Data1", () => {
   getData("Data2", () => {
      getData("Data2", () => {
         getData("Data3", () => {
            getData("Data4", () => {
               getData("Data5", () => {
                  getData("Data6", () => {
                     console.log("Done");                     
                  })
               })
            })
         })
      })
   })
})