Để trở thành một JavaScript Developer giỏi, các bạn không chỉ cần nắm vững cú pháp và các phương pháp cơ bản, mà còn phải hiểu sâu về những khái niệm quan trọng giúp tối ưu hóa hiệu suất và nâng cao chất lượng mã nguồn. Trong bài viết này, VietnamWorks inTECH sẽ giới thiệu 25 khái niệm quan trọng mà mọi JavaScript Developer nên biết, từ các kỹ thuật cơ bản đến những mẹo nâng cao, giúp bạn trở thành một lập trình viên JavaScript xuất sắc.

1. Call Stack

Trong JavaScript, callback là một hàm được truyền vào một hàm khác dưới dạng tham số và sẽ được gọi hoặc thực thi bên trong hàm đó. Điều này có nghĩa là một hàm sẽ đợi hàm khác hoàn thành hoặc trả về giá trị trước khi tiếp tục, tạo thành một chuỗi các hành động (khi X hoàn thành, thì Y sẽ được thực hiện, và cứ tiếp tục như vậy). Đây là lý do callback thường được sử dụng trong các tác vụ bất đồng bộ của JavaScript để giúp các hành động diễn ra tuần tự.

Ví dụ: 

const greeting = (name) => {

   console.log('Hello ' + name);

}

const processUserName = (callback) => {

   name = 'VietnamWorksinTECH';

   callback(name);

}

processUserName(greeting);

Output

Hello VietnamWorksinTECH

Trong ví dụ trên, hãy chú ý rằng hàm 'greeting' được truyền vào hàm 'processUserName' dưới dạng một đối số (callback). Trước khi hàm 'greeting' được thực thi, nó phải đợi sự kiện 'processUserName' hoàn thành trước.

2. Dữ liệu nguyên thủy (Primitive types)

Tất cả các kiểu dữ liệu, ngoại trừ đối tượng, đều được định nghĩa các giá trị bất biến (tức là các giá trị không thể thay đổi). Ví dụ, khác với C, chuỗi (String) trong JavaScript là bất biến. Chúng ta gọi các giá trị thuộc các kiểu này là “giá trị nguyên thủy”.

JavaScript có sáu kiểu dữ liệu nguyên thủy: số (numbers), chuỗi (strings), boolean, null, undefined và symbols. Những kiểu này biểu diễn các giá trị đơn giản và đều là bất biến. Việc hiểu rõ cách hoạt động của các kiểu dữ liệu này rất quan trọng để viết code hiệu quả và tránh lỗi.

const age = 25; // Number

const name ="Ann"; // String

const isDeveloper = true; // Boolean

let emptyValue = null; // Null

let notDefined; // Undefined

3. Kiểu giá trị và kiểu tham chiếu (Value Types và Reference Types)

Trong JavaScript, biến có thể chứa hai loại: kiểu giá trị và kiểu tham chiếu. Các kiểu giá trị bao gồm số (numbers), chuỗi (strings), boolean — chúng lưu trữ trực tiếp dữ liệu. Còn các kiểu tham chiếu (như đối tượng và mảng) thì lưu trữ một tham chiếu trỏ đến dữ liệu.

// Value Type

let num1 = 42;

let num2 = num1; // num2 gets a copy of num1

// Reference Type

let arr1 = [1, 2, 3];

let arr2 = arr1; // arr2 references the same array as arr1

Khi bạn thay đổi num1, nó không ảnh hưởng đến num2. Tuy nhiên, việc sửa đổi arr1 sẽ thay đổi arr2 vì chúng trỏ đến cùng một mảng.

4. Implicit, Explicit, Nominal, Structuring và Duck Typing

4.1. Kiểu ngầm định (Implicit Typing)

Kiểu ngầm định là khi ngôn ngữ lập trình tự động suy luận kiểu dữ liệu của một biến dựa trên giá trị nó được gán, mà không cần khai báo kiểu rõ ràng.

Ví dụ: Trong JavaScript hoặc Python, bạn không cần khai báo kiểu dữ liệu của biến trước. Ngôn ngữ tự xác định dựa trên giá trị.

let x = 42; // x được hiểu là kiểu number

4.2. Kiểu tường minh (Explicit Typing)

Ngược lại với kiểu ngầm định, kiểu tường minh yêu cầu lập trình viên phải khai báo rõ ràng kiểu dữ liệu của biến.

JavaScript không hỗ trợ kiểu tường minh theo cách thông thường như C++ hay Java. Tuy nhiên, bạn có thể dùng TypeScript (một phiên bản mở rộng của JavaScript) để khai báo kiểu rõ ràng:

let x: number = 42;        // Khai báo x là kiểu number

let y: string = "Hello";   // Khai báo y là kiểu string

4.3. Kiểu định danh (Nominal Typing)

Kiểu định danh là khi hai kiểu dữ liệu chỉ được xem là tương đương nếu chúng có cùng tên hoặc định danh rõ ràng.

JavaScript không hỗ trợ Kiểu định danh, nhưng TypeScript cũng có phần nào hỗ trợ thông qua classes hoặc interfaces. Hai đối tượng chỉ có thể được coi là tương đương nếu chúng chia sẻ cùng một loại hoặc giao diện (interface).

4.4. Kiểu cấu trúc (Structural Typing)

Trong kiểu cấu trúc, các kiểu dữ liệu được coi là tương đương nếu chúng có cùng cấu trúc hoặc hình dạng, bất kể tên gọi của chúng.

4.5. Kiểu vịt (Duck Typing)

Duck typing dựa trên ý tưởng rằng: "Nếu nó kêu như vịt và đi như vịt, thì nó là vịt." Nghĩa là một đối tượng thuộc một kiểu nào đó nếu nó có các phương thức hoặc thuộc tính mà kiểu đó yêu cầu, bất kể đối tượng đó có khai báo rõ ràng là thuộc kiểu đó hay không.

JavaScript là một ngôn ngữ theo kiểu "Duck Typing." Nếu một đối tượng có các phương thức hoặc thuộc tính mà ta mong đợi, nó có thể được sử dụng như một đối tượng thuộc kiểu đó.

Như vậy, thông qua Duck Typing, chỉ cần đối tượng có phương thức quack(), nó sẽ được coi là một “con vịt” trong ngữ cảnh này, bất kể loại hay tên của đối tượng đó.

function makeItQuack(duck) {
    if (duck.quack) {
        duck.quack();
    } else {
        console.log("This is not a duck!");
    }
}

let duck = {
    quack: function() {
        console.log("Quack!");
    }
};

let notADuck = {
    say: function() {
        console.log("I am not a duck.");
    }
};

makeItQuack(duck);      // Output: Quack!
makeItQuack(notADuck);  // Output: This is not a duck!

5. == vs === vs typeof

JavaScript cung cấp hai toán tử so sánh: == (so sánh lỏng lẻo) và === (so sánh nghiêm ngặt). Việc nắm rõ sự khác biệt giữa chúng là rất quan trọng. Ngoài ra, còn có toán tử typeof, giúp xác định kiểu dữ liệu của một giá trị, điều này hữu ích khi làm việc với dữ liệu động. Trong đó:

  • Toán tử == (loose equality): so sánh giá trị của hai đối tượng sau khi chuyển đổi kiểu (type coercion) nếu cần thiết.

  • Toán tử === (strict equality): so sánh cả giá trị và kiểu dữ liệu của hai đối tượng, không thực hiện chuyển đổi kiểu.

  • Toán tử typeof:một công cụ hữu ích để kiểm tra kiểu dữ liệu của một giá trị, đặc biệt khi làm việc với dữ liệu động.

Hiểu rõ cách hoạt động của ==, ===typeof sẽ giúp bạn tránh những lỗi khó đoán và cải thiện hiệu quả khi làm việc với JavaScript.

console.log(5 =="5"); // true (loose equality)

console.log(5 ==="5"); // false (strict equality)

console.log(typeof"Hello"); // "string'

6. Phạm vi hàm, phạm vi khối và phạm vi từ vựng

Trong JavaScript, việc sử dụng phạm vi hàm (function scope) và phạm vi khối (block scope) giúp quản lý biến và kiểm soát phạm vi truy cập của chúng. Đây là những khái niệm quan trọng trong lập trình để đảm bảo code của bạn hoạt động đúng và dễ duy trì. 

  • Phạm vi hàm (Function Scope): Biến được khai báo bên trong một hàm chỉ có thể truy cập được trong chính hàm đó. Phạm vi hàm đảm bảo rằng biến không bị ảnh hưởng bởi các phần khác trong code ngoài hàm. 

  • Phạm vi khối (Block Scope): Phạm vi khối, được giới thiệu với let và const, giới hạn biến chỉ trong khối mã được bao bọc bởi dấu ngoặc nhọn {}. Điều này giúp ngăn chặn các biến bị ảnh hưởng ngoài khối đó.

Phạm vi từ vựng (lexical scope) là một khái niệm quan trọng trong lập trình, bao gồm việc xác định khả năng truy cập các biến và hàm trong phạm vi nơi chúng được khai báo.

7. Expression vs Statement

Biểu thức là thứ tạo ra giá trị, trong khi câu lệnh thực hiện hành động.

// Expression

let sum = 3 + 4;

// Statement

if (sum === 7) {

console.log("The sum is 7.");

}

 

Biểu thức có thể là một phần của câu lệnh, như 3 + 4 bên trong câu lệnh if. Phân biệt chúng có thể giúp bạn hiểu cấu trúc code tốt hơn.

8. IIFE, Modules và Namespaces

IIFE(Biểu thức hàm được gọi ngay lập tức), Modules và Namespaces đóng góp vào việc tổ chức và đóng gói code.

// IIFE (Immediately Invoked Function Expression)

(function () {

 const privateVar = "I’m private!";

 console.log(privateVar);

})();

// Modules

// module.js

export function add(a, b) {

 return a + b;

}

// main.js

import { add } from "./module.js";

console.log(add(2, 3));

// Namespaces

const myNamespace = {

 variable: "I'm in a namespace",

 log() {

   console.log(this.variable);

 }

};

myNamespace.log();

IIFE đảm bảo phạm vi riêng tư, các module tạo điều kiện thuận lợi cho việc tổ chức code theo module  và namespaces ngăn ngừa xung đột biến toàn cục.

9. Message Queue và Event Loop

JavaScript thực thi code theo cách hướng sự kiện (event-driven) thông qua vòng lặp sự kiện (event loop). Hàng đợi sự kiện (message queue) giữ các sự kiện để được xử lý khi đã sẵn sàng.

console.log("Start");

setTimeout(() => {

console.log("Inside Timeout");

}, 1000);

console.log("End");

Mặc dù setTimeout được thiết lập để chạy sau 1 giây, nhưng dòng "End" vẫn được in ra trước. Điều này xảy ra vì cơ chế xử lý bất đồng bộ trong JavaScript thông qua vòng lặp sự kiện (event loop) và hàng đợi sự kiện (message queue).

10. setTimeout, setInterval và requestAnimationFrame

Các hàm như setTimeout, setInterval, và requestAnimationFrame là những công cụ quan trọng trong JavaScript để quản lý các tác vụ bất đồng bộ và hoạt ảnh.

// setTimeout

setTimeout(() => {

 console.log("Delayed log");

}, 1000);

// setInterval

const intervalId = setInterval(() => {

 console.log("Repeated log");

}, 2000);

// requestAnimationFrame

function animate() {

 console.log("Animating…");

 requestAnimationFrame(animate);

}

animate();

setTimeout lên lịch cho một hàm chạy sau một khoảng thời gian trì hoãn được chỉ định, setInterval gọi lại một hàm lặp đi lặp lại với một khoảng thời gian cố định, cuối cùng requestAnimationFrame thường được sử dụng để tạo hoạt ảnh mượt mà và hiệu quả.

11. Toán tử Bitwise, Type Arrays và Array Buffers

Về cơ bản, với máy tính, mọi thứ đều được chuyển thành các số 1 và 0. Máy tính không làm việc trực tiếp với số, ký tự hay chuỗi, mà chỉ dùng các số nhị phân (bit). Nói đơn giản, tất cả đều được lưu dưới dạng nhị phân. Sau đó, máy tính dùng các bảng mã như UTF-8 để chuyển các tổ hợp bit thành ký tự, số hoặc các ký hiệu khác.

Trong JavaScript, toán tử bitwise, typed arrays và array buffers là những công cụ mạnh mẽ giúp lập trình viên làm việc với dữ liệu ở mức thấp hơn, đặc biệt khi xử lý dữ liệu nhị phân hoặc tối ưu hóa hiệu suất. Trong đó,

  • Toán tử bitwise: Xử lý dữ liệu nhị phân ở cấp độ bit.

  • Typed Arrays: Quản lý và làm việc với dữ liệu kiểu cố định, giúp tối ưu hóa bộ nhớ và xử lý dữ liệu nhị phân.

  • Array Buffers: Lưu trữ dữ liệu nhị phân thô, cung cấp cách quản lý bộ nhớ hiệu quả khi làm việc với dữ liệu lớn.

12. DOM và Layout Trees

Document Object Model (DOM) là một phần quan trọng giúp trang web có thể tương tác được. Nó là một giao diện cho phép lập trình viên thay đổi nội dung, cấu trúc và kiểu dáng của trang web.

// Get references to the necessary DOM elements

const commentsListElement = document.getElementById('commentsList');

const postCommentButton = document.getElementById('postComment');

 

// Attach an event listener to the "Post Comment" button

postCommentButton.addEventListener('click', function() {

 // Create a new comment item

 const comment = document.createElement('li');

 comment.textContent = 'New comment';

 

 // Append the new comment to the comments list

 commentsListElement.appendChild(comment);

});

Layout Tree là một cây dữ liệu được xây dựng bởi trình duyệt trong quá trình layout (bố cục) của trang web. Nó chứa các thông tin về cách các phần tử HTML trên trang sẽ được hiển thị và bố trí trên màn hình.

13. Factories và Classes

JavaScript cung cấp hai cách chính để tạo đối tượng: factories (nhà máy) và classes (lớp).

  • Factories (Nhà máy) là các hàm tạo ra và trả về các đối tượng. Chúng cho phép bạn tạo nhiều đối tượng với các thuộc tính và phương thức giống nhau.

  • Classes (Lớp) cung cấp một cấu trúc rõ ràng và hướng đối tượng hơn. Bạn có thể định nghĩa lớp với các thuộc tính và phương thức, sau đó tạo ra các đối tượng từ lớp đó.

// Factory

function createPerson(name, age) {

 return {

   name,

   age,

   greet() {

     console.log(`Hello, my name is ${this.name}.`);

   },

 };

}

// Class

class Person {

 constructor(name, age) {

   this.name = name;

   this.age = age;

 }

 greet() {

   console.log(`Hello, my name is ${this.name}.`);

 }

}

const person1 = createPerson("John", 25);

const person2 = new Person("Alice", 30);

person1.greet();

person2.greet();

Mặc dù cả hai cách đều đạt được kết quả giống nhau, nhưng classes (lớp) cung cấp một phương pháp có tổ chức và mang tính chuẩn mực hơn để cấu trúc các đối tượng, đặc biệt là trong các tình huống liên quan đến kế thừa.

14. this, call, apply và bind

Trong JavaScript, các phương thức call, apply, và bind được sử dụng để kiểm soát giá trị của từ khóa this trong một hàm. Chúng cung cấp sự linh hoạt trong việc chỉ định ngữ cảnh mà hàm nên thực thi.

const person = {

 name: "Lingling",

 greet(message) {

   console.log(`${message}, ${this.name}!`);

 }

};

const anotherPerson = {

 name: "Orm",

};

// call

person.greet.call(anotherPerson, "Hello");

// apply

person.greet.apply(anotherPerson, ["Hi"]);

// bind

const greetOrm = person.greet.bind(anotherPerson, "Hey");

greetOrm();

Những phương thức này đặc biệt hữu ích trong các tình huống khi bạn muốn xác định rõ giá trị của this khi gọi một hàm.

15. new, Constructor, instanceof và Instances

Những khái niệm này là cốt lõi trong lập trình hướng đối tượng trong JavaScript.

new: Tạo một instance của constructor hoặc class.

Constructor: Hàm được gọi khi tạo một object mới, có thể là constructor function hoặc class constructor.

instanceof: Kiểm tra một object có phải là instance của một constructor/class hay không.

Instance: Là object được tạo từ constructor hoặc class.

16. Kế thừa nguyên mẫu và chuỗi nguyên mẫu

JavaScript sử dụng kế thừa dựa trên prototype, trong đó các đối tượng có thể kế thừa các thuộc tính và phương thức từ các đối tượng khác thông qua Prototype Chain (chuỗi prototype).

// Prototype

function Animal(name) {

 this.name = name;

}

Animal.prototype.sound = function () {

 console.log("Some generic sound");

};

// Inheritance

function Dog(name, breed) {

 Animal.call(this, name);

 this.breed = breed;

}

Dog.prototype = Object.create(Animal.prototype);

Dog.prototype.constructor = Dog;

Dog.prototype.sound = function () {

 console.log("Bark!");

};

const myDog = new Dog("Buddy", "Labrador");

myDog.sound(); // Bark!

Hiểu được tính kế thừa nguyên mẫu là rất quan trọng để tạo ra các cấu trúc code hiệu quả và có thể tái sử dụng trong JavaScript.

17. Object.create và Object.assign

Object.create(): Tạo một object mới với một prototype cụ thể (thiết lập kế thừa prototype).

Object.assign(): Sao chép thuộc tính từ một hoặc nhiều object vào một object đích, thực hiện sao chép nông các thuộc tính.

18. map, reduce, filter

map, reducefilter là ba phương thức rất hữu ích trong JavaScript để xử lý mảng. Chúng cho phép bạn thao tác và biến đổi dữ liệu một cách dễ dàng và hiệu quả.

  • map(): Dùng để biến đổi từng phần tử trong mảng thành một giá trị mới, trả về một mảng mới.

  • filter(): Dùng để lọc ra các phần tử thỏa mãn điều kiện, trả về một mảng mới chứa các phần tử đó.

  • reduce(): Dùng để giảm mảng thành một giá trị duy nhất bằng cách áp dụng hàm callback cho từng phần tử.

19. Pure Functions, Side Effects, State Mutation và Event Propagation

Hàm thuần (Pure Functions) là hàm mà với cùng một đầu vào, nó luôn trả về kết quả giống nhau mà không gây ra tác động phụ nào. Điều này có nghĩa là hàm này không phụ thuộc vào, hay thay đổi, bất kỳ trạng thái hoặc dữ liệu bên ngoài nào. 

Tác động phụ (Side Effects): Tác động phụ xảy ra khi một hàm thay đổi một trạng thái bên ngoài hoặc tương tác với thế giới bên ngoài. Ví dụ về tác động phụ:

  • Thay đổi biến toàn cục.

  • Thực hiện các yêu cầu mạng (gọi HTTP).

  • Thao tác với DOM.

  • Ghi vào tệp hoặc cơ sở dữ liệu.

Thay đổi trạng thái (State Mutation): Thay đổi trạng thái đề cập đến việc sửa đổi trạng thái hiện tại của một biến, đối tượng hoặc cấu trúc dữ liệu. Trong JavaScript, điều này thường xảy ra khi một đối tượng hoặc mảng bị thay đổi sau khi đã được tạo, theo đó có thể dẫn đến hành vi khó đoán nếu không được quản lý tốt.

Lan truyền sự kiện (Event Propagation): Lan truyền sự kiện mô tả cách sự kiện di chuyển qua DOM khi được kích hoạt. Nó bao gồm ba giai đoạn:

  • Giai đoạn bắt sự kiện (Capturing phase): Sự kiện bắt đầu từ gốc và lan truyền xuống đến phần tử đích.

  • Giai đoạn mục tiêu (Target phase): Sự kiện đến phần tử đích.

  • Giai đoạn nổi lên (Bubbling phase): Sự kiện lan truyền ngược từ phần tử đích trở lại gốc.

20. Closures

Closure xảy ra khi một hàm giữ quyền truy cập vào các biến từ phạm vi bên ngoài của nó, ngay cả sau khi hàm bên ngoài đã hoàn thành thực thi. Khái niệm này rất quan trọng để tạo ra các biến riêng tư và duy trì trạng thái giữa các lần gọi hàm.

function outerFunction() {

 let outerVar = "I’m from outside!";

function innerFunction() {

   console.log(outerVar);

 }

 return innerFunction;

}

const closureExample = outerFunction();

closureExample(); // Prints: I'm from outside!

Trong ví dụ này, innerFunction "đóng gói" (closes over) biến outerVar, cho phép nó truy cập outerVar ngay cả sau khi outerFunction đã hoàn tất thực thi.

21. Các hàm bậc cao (High order functions)

Đây là các hàm có thể nhận hàm khác làm tham số hoặc trả về một hàm. Chúng giúp code dễ đọc hơn và dễ duy trì hơn bằng cách áp dụng cách viết theo kiểu lập trình hàm.

const numbers = [1, 2, 3, 4, 5];

// Map - Applies a function to each element in an array

const squared = numbers.map((num) => num * num);

// Filter - Creates a new array with elements that satisfy a condition

const evenNumbers = numbers.filter((num) => num % 2 === 0);

// Reduce - Reduces an array to a single value

const sum = numbers.reduce((acc, num) => acc + num, 0);

console.log(squared); // [1, 4, 9, 16, 25]

console.log(evenNumbers); // [2, 4]

console.log(sum); // 15

Hàm bậc cao cho phép viết code ngắn gọn và rõ ràng hơn khi làm việc với mảng và các phép biến đổi dữ liệu.

22. Collections và Generators

JavaScript cung cấp nhiều loại collections, như mảng (arrays) và tập hợp (sets), để lưu trữ và quản lý nhiều giá trị. Generators là các hàm đặc biệt có thể tạm dừng và tiếp tục, cho phép lặp qua dữ liệu một cách linh hoạt và hiệu quả hơn.

// Array

const colors = ["red", "green", "blue"];

// Set

const uniqueColors = new Set(colors);

// Generator

function* generateNumbers() {

 yield 1;

 yield 2;

 yield 3;

}

const numbersGenerator = generateNumbers();

console.log([…uniqueColors]); // ["red", "green", "blue"]

console.log([…numbersGenerator]); // [1, 2, 3]

Collections giúp tổ chức và xử lý dữ liệu, trong khi generators hỗ trợ tạo ra các chuỗi dữ liệu có thể lặp qua, đặc biệt hữu ích cho việc tải dữ liệu theo cách lười (lazy-loading) hoặc xử lý các tập dữ liệu lớn.

23. Promises

Chúng ta đã hiểu khái niệm về callback, nhưng điều gì sẽ xảy ra nếu code của bạn có các callback lồng vào nhau liên tục? Cấu trúc đệ quy của callback này được gọi là ‘callback hell’promises được thiết kế để giúp giải quyết vấn đề này.

Promises rất hữu ích trong các thao tác bất đồng bộ của JavaScript khi chúng ta cần thực hiện hai hoặc nhiều thao tác nối tiếp nhau (hoặc chuỗi các callback), nơi mỗi hàm tiếp theo bắt đầu khi hàm trước đó đã hoàn thành.

Một promise là một đối tượng có thể sản sinh một giá trị duy nhất vào một thời điểm trong tương lai, có thể là một giá trị đã được giải quyết (resolved) hoặc lý do vì sao nó không được giải quyết (rejected). Theo developer.mozilla, “Một Promise là một đối tượng đại diện cho việc hoàn thành hoặc thất bại cuối cùng của một thao tác bất đồng bộ. Về cơ bản, một promise là một đối tượng được trả về mà bạn gán các callback vào, thay vì truyền các callback vào một hàm.”

Promises giải quyết vấn đề của ‘callback hell’, tức là cấu trúc đệ quy của các callback (callback lồng nhau trong callback và tiếp tục như vậy).

Một promise có thể ở ba trạng thái khác nhau:

1. Fulfilled (Hoàn thành): Khi thao tác đã được thực hiện thành công.

2. Rejected (Bị từ chối): Khi thao tác không thành công.

3. Pending (Đang chờ): Trạng thái ban đầu, chưa hoàn thành hoặc bị từ chối.

const promise = new Promise((resolve, reject) => {

   isNameExist = true;

   if (isNameExist) {

       resolve("User name exist")

   } else {

       reject("error")

   }

})

promise.then(result => console.log(result)).catch(() => {

           console.log(‘error!')

           })

Output:

User name exist

Promise {<resolved>: undefined}

Xem xét đoạn code trên với một ví dụ promise giả định như việc thực hiện thao tác isNameExist một cách bất đồng bộ. Trong promise, các đối số là hai hàm: resolvereject. Nếu thao tác thành công, tức là isNameExist trả về true, promise sẽ được giải quyết (resolved) và hiển thị kết quả "User name exists". Ngược lại, nếu thao tác thất bại, promise sẽ bị từ chối (rejected) và hiển thị kết quả "Error!".

24. async/await

Async & await là cú pháp mới được xây dựng trên promises và giống như promises, nó cũng cung cấp cách duy trì hoạt động bất đồng bộ một cách đồng bộ hơn. Trong JavaScript, các thao tác bất đồng bộ có thể được xử lý qua các phiên bản khác nhau:

  • ES5 -> Callback

  • ES6 -> Promise

  • ES7 -> Async & Await

Bạn có thể sử dụng async/await để thực hiện các yêu cầu Rest API khi bạn muốn dữ liệu được tải hoàn toàn trước khi đưa nó vào giao diện. Đối với lập trình viên Node.js, async/await là một cải tiến cú pháp tuyệt vời. Nó giúp các developer thực hiện lập trình hàm trong JavaScript và cũng tăng cường khả năng đọc code.

Ví dụ:

const showPosts = async ( ) => {

   const response = await fetch (* https : //jsonplacenolder.typicode.com/posts');

   const posts = await response. json ();

   console .1og(posts) ;

 }

 showPosts ();

Output

Để cho JavaScript biết rằng chúng ta đang làm việc với promises, chúng ta cần đặt await bên trong một hàm async. Trong ví dụ trên, chúng ta phải đợi hai điều: phản hồi từ máy chủ và dữ liệu từ phản hồi đó. Trước khi chuyển đổi phản hồi thành định dạng JSON, chúng ta cần đảm bảo rằng phản hồi đã được nhận, nếu không, chúng ta có thể gặp lỗi khi cố gắng xử lý dữ liệu chưa có.

Lời kết

Những khái niệm mà chúng ta đã khám phá trong bài viết này là những công cụ thiết yếu giúp bạn nâng cao kỹ năng lập trình JavaScript của mình. Việc hiểu rõ và áp dụng chúng vào thực tế sẽ giúp bạn giải quyết các thách thức trong phát triển phần mềm một cách hiệu quả và tự tin hơn.

VietnamWorks inTECH

TẠO TÀI KHOẢN MỚI: XEM FULL “1 TÁCH CODEFEE” - NHẬN SLOT TƯ VẤN CV TỪ CHUYÊN GIA - CƠ HỘI RINH VỀ VOUCHER 200K