Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save nguyentien98/4f13fa27d01f7148045bcad8d521820f to your computer and use it in GitHub Desktop.
Save nguyentien98/4f13fa27d01f7148045bcad8d521820f to your computer and use it in GitHub Desktop.

Call stack là gì

Call stack là một cấu trúc dữ liệu với đặc điểm là: FILO(First in - Last out). Trong Javascript, call stack được sử dụng để quản lý thứ tự các câu lệnh thực thi.

Trường hợp 1

/* Trong file main.js */

var firstFunction = function () {  
  console.log("I'm first!");
  console.log("I'm second!");
};

firstFunction();

/* Kết quả:
 * => I'm first!
 * => I'm second!
 */

Giải thích cơ bản:

Step 1: Chạy file main.js -> cho vào call stack


main.js


Step 2: Hàm firstFunction() được gọi -> cho vào stack


firstFunction()

main.js


Step 3: Trong firstFunction() gọi đến hàm console.log("I'm first!") đầu tiên nên nó cũng cho vào stack


console.log("I'm first!")

firstFunction()

main.js


Step 3: console.log("I'm first!") sẽ thực thi và trả về 1 đoạn log -> xóa khỏi stack


firstFunction()

main.js


Step 4: Sau đó đến hàm console.log("I'm second!") nó cũng được cho vào stack


console.log("I'm second!")

firstFunction()

main.js


Step 5: console.log("I'm second!") sẽ thực thi và trả về 1 đoạn log -> xóa khỏi stack


firstFunction()

main.js


Step 5: firstFunction() không còn lệnh nào nữa -> xóa khỏi stack


main.js


Step 6: Hàm main.js không còn lệnh nào nữa -> xóa khỏi stack



Step 7: Stack rỗng -> Kết thúc.

Trường hợp 2

/* Trong file main.js */

var firstFunction = function () {  
  console.log("I'm first!");
};

var secondFunction = function () {  
  firstFunction();
  console.log("I'm second!");
};

secondFunction();

/* Kết quả:
 * => I'm first!
 * => I'm second!
 */

Giải thích cách đoạn code trong file main.js được thực thi:

Step 1: Chạy file main.js: cho vào call stack

Step 2: Quét từ trên xuống dưới, thấy hàm secondFunction() được gọi: cho hàm vào call stack

Step 3: Chạy vào hàm secondFunction() nó sẽ thấy firstFunction() được gọi: cho firstFunction() vào call stack.

Step 4: Sau khi cho vào call stack nó sẽ bắt đầu thực thi từ trên xuống theo nguyên lý FILO: firstFunction(): trả về console.log("I'm first!") -> không còn lệnh nào -> xóa khỏi call stack

Step 5: Tiếp theo là secondFunction() nó sẽ chạy lệnh console.log("I'm second!"). Sau khi ko còn dòng lệnh nào nữa thì nó cũng bị xóa khỏi call stack.

Step 5: Main.js không còn dòng lệnh nào -> xóa khỏi call stack và kết thúc.

  • Theo những bước và nguyên lý của call stack ở trên thì javascript chạy hàm từ trên xuống 1 cách đồng bộ và Javascript là 1 ngôn ngữ đơn luồng. Tức là trong 1 thời điểm không thể thực hiện nhiều công việc.

  • Vậy thì tại sao lại sinh ra bất đồng bộ? Vì các Engine Javascript đặc biệt là V8 Engine được sử dụng trên các Browser cho việc thông dịch Javascript. Giả sử như 1 button được gọi và nó có chứa 1 hàm setInterval và có thời gian là 5 phút chẳng hạn. Điều đó có nghĩa là trong 5 phút người dùng ko thể bấm vào bất cứ button nào khác và toàn bộ Javascript phải dừng lại. Điều này thật ngu ngốc, chính vì vậy trong Javascript có khái niệm bất đồng bộ.

  • Thật ra Javascript là đơn luồng và bất đồng bộ không phải 1 phần của Javascript. Nó là API của trình duyệt (hoặc trong môi trường development). Nó được cung cấp để giải quyết vấn đề bên trên.

Event loop và bất đồng bộ trong Javascript

function main(){
  console.log('A');
  setTimeout(
    function display(){ console.log('B'); }
  ,0);
	console.log('C');
}

main();

//	Output
//	A
//	C
//  B

Giải thích khối lệnh

Step 1: Hàm main được gọi -> cho vào stack

Step 2: console.log('A') được gọi -> cho vào stack -> thực hiện xong -> xóa khỏi stack

Step 3: Hàm setTimeout được gọi -> cho vào stack. Tuy nhiên hàm này sứ dụng Web APIs để delay hàm callback. Nó được browser đẩy vào trong 1 vùng (Tạm gọi là Event table) để chờ.

Step 4: console.log('C') được gọi -> cho vào stack -> thực hiện xong -> xóa khỏi stack Trong cùng thời điểm này, setTimeout hết thời gian chờ -> đẩy vào Event Queue.

Step 5: Hàm main hết dòng lệnh -> xóa khỏi stack

Step 6: Event Loop là phần trong trình duyệt, nó sẽ liên tục chạy và quét để kiếm tra có Event Queue nào đang đợi hay không. Lúc này nó thấy có hàm callback đang đợi -> chuyển display() vào stack -> thực thi -> xóa khỏi stack

Step 7: stack rỗng.

Note: Điều đó kết luận lại rằng:

  1. Bất đồng bộ là 1 Web API, nó dùng để giải quyết những vấn đề ở bên trên cùng.
  2. Javascript vẫn sẽ thực hiện từ trên xuống, đơn luồng, blocking Xem thêm tại mục Code snippet 2
  3. Bất đồng bộ trở thành điều không thể thiếu trong javascript.

Tại sao code này lại không thể bắt được lỗi

function myApiFunc(callback) {
  /*
   * This pattern does NOT work!
   */
  try {
    doSomeAsynchronousOperation((err) => {
      if (err) {
        throw (err);
      }
      /* continue as normal */
    });
  } catch (ex) {
    callback(ex);
  }
}

Code này không thể bắt được lỗi bởi vì nó xử lý các bước như sau:

Step 1: myApiFunc() được gọi -> cho vào stack

Step 2: doSomeAsynchronousOperation được gọi, tuy nhiên nó là hàm bất đồng bộ nên nó bị đẩy vào event table và đợi để cho vào event queue

Step 3: Ko xảy ra lỗi gì và không còn lệnh bên dưới vì vậy myApiFunc() bị xóa khỏi stack -> stack rỗng

Step 4: Lúc này callback function trong hàm doSomeAsynchronousOperation() được event loop nhận biết -> cho vào stack -> lỗi được throw -> crash

Cách giải quyết

  1. try...catch trong callback
function myApiFunc(callback) {
  /*
   * This pattern does NOT work!
   */
  
    doSomeAsynchronousOperation((err) => {
	  try {
		  if (err) {
			throw (err);
		  }
	  } catch (ex) {
		callback(ex);
	  }
      /* continue as normal */
    });
  
}
  1. Dùng async/await
async function myApiFunc(callback) {
  /*
   * This pattern does NOT work!
   */
  try {
    await doSomeAsynchronousOperation();
  } catch (ex) {
    callback(ex);
  }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment