Mã JavaScript có chạy tuần tự không?

Cách dễ nhất để hiểu mã không đồng bộ là nhận ra rằng mã không thực thi tuần tự. Điều này có thể khó hiểu trong JavaScript, đặc biệt nếu bạn đến từ ngôn ngữ lập trình mặc định là đồng bộ hoặc tuần tự, như PHP

Trong bài đăng này, bạn sẽ học cách viết mã async [còn được gọi là 'không tuần tự'] trong JavaScript một cách hiệu quả. Bạn sẽ tìm hiểu kiến ​​thức cơ bản về cách sử dụng lệnh gọi lại, lời hứa và kiểu async/await hiện đại

Bắt đầu nào

Lập trình không đồng bộ. Giới thiệu nhanh

Hãy bắt đầu với những điều cơ bản. Có hai mô hình thực thi trong các ngôn ngữ lập trình. đồng bộ và không đồng bộ

Mô hình đồng bộ là nơi dòng mã tiếp theo không được thực thi cho đến khi dòng mã hiện tại được thực hiện. Ngay cả khi dòng mã hiện tại gọi API phản hồi trong 500 mili giây hoặc đọc tệp 100 MB, quá trình thực thi sẽ đợi cho đến khi dòng mã hoàn tất. Nói cách khác, trong thực thi đồng bộ, mọi thứ xảy ra lần lượt, cái này nối tiếp cái kia

Mặt khác, nếu có năm dòng mã và dòng thứ hai gọi một API, thì nó sẽ được đẩy xuống nền với cơ chế cho phép thực thi chính biết rằng API đã phản hồi

Trong khi điều đó đang xảy ra, các dòng từ ba đến năm cũng đang được thực thi. Đây là lời giải thích quá đơn giản về mô hình thực thi mã không đồng bộ hoặc không đồng bộ. Trong trường hợp này, mã không chạy từng dòng theo thứ tự. Mọi thứ có thể được đặt ở chế độ nền [hoặc hàng đợi] và sau đó sẽ biết kết quả. Mô hình thực thi này cho phép nhiều việc xảy ra cùng một lúc

Khái niệm về mô hình thực thi là điều cần thiết ở đây bởi vì, tùy thuộc vào ngôn ngữ lập trình, nó có thể hoạt động theo cách đồng bộ hoặc không đồng bộ. Ví dụ: trong Python, bạn có thể viết chương trình theo kiểu đồng bộ hoặc không đồng bộ — trong khi JavaScript là không đồng bộ theo mặc định

Làm sao? . Trước tiên, hãy xem một ví dụ cơ bản, với một thực thi đồng bộ đơn giản hiển thị nhật ký

console.log["first log line"];
console.log["second log line"];
console.log["third log line"];

Đoạn mã trên khá đơn giản và sẽ lần lượt in ba dòng nhật ký, như bên dưới

first log line
second log line
third log line

Bây giờ, hãy làm cho nó thú vị hơn một chút. Xem mã JavaScript không thực thi tuần tự với ví dụ 'chờ 1 giây'

console.log["first log line"];
 
// 1 second wait
setTimeout[function [] {
  console.log["third log line - after 1 second"];
}, 1000];
 
console.log["second log line"];

Bây giờ, khi đoạn mã trên được thực thi, đầu ra như sau

first log line
second log line
third log line - after 1 second

Vì vậy, những gì xảy ra ở đây? . Nó được đặt thành 1 giây — 1000 ms. Vì vậy, mã này được đẩy xuống nền để được thực thi sau 1 giây

Sau đó, bảng điều khiển

first log line
second log line
third log line
3 được thực thi và thực hiện công việc của nó. Sau 1 giây,
first log line
second log line
third log line
4 trong setTimeout thực thi, dẫn đến đầu ra ở trên. Đây là cách mã không đồng bộ hoạt động trong JavaScript

Trong phần tiếp theo, chúng ta sẽ tìm hiểu về các cuộc gọi lại với JavaScript không đồng bộ bằng cách sử dụng một ví dụ thú vị về Github và Twitter

Gọi lại với Async JavaScript

“Gọi lại” là một từ bạn đã từng nghe trong đời thực. bạn gọi cho một người bạn, nhưng bạn của bạn không bắt máy. Bạn đến hộp thư thoại của họ và để lại tin nhắn để họ gọi lại cho bạn. Bạn của bạn nghe thấy tin nhắn và giả sử hai giờ sau, họ gọi lại cho bạn. Khái niệm này giống nhau trong lập trình và JavaScript, đặc biệt là với thực thi không đồng bộ

Chúng tôi sẽ không đi vào chi tiết về các sự kiện và cách các hàm có thể được truyền dưới dạng tham số trong Javascript — đó là một chủ đề nằm ngoài phạm vi của bài viết này. Một điều cần đề cập là các cuộc gọi lại cũng có thể được sử dụng với mã đồng bộ

Để hiểu các cuộc gọi lại trong ngữ cảnh mã không đồng bộ, hãy xem một ví dụ về mã được viết cho trình duyệt. Trong đoạn mã này, bạn sẽ tìm nạp Tweet cuối cùng từ một người chỉ bằng cách sử dụng tên người dùng GitHub của họ

Đầu tiên, bạn sẽ gọi API GitHub để lấy thông tin chi tiết của người dùng, bao gồm cả tên người dùng Twitter của họ. Sau đó, bạn sẽ gọi nguồn cấp dữ liệu RSS của Nitter để nhận tweet cuối cùng của họ

Chúng tôi sẽ không sử dụng API Twitter chính thức vì điều này liên quan đến xác thực và làm phức tạp quá trình. Với mục tiêu rõ ràng của chúng tôi, chúng tôi sẽ sử dụng mã bên dưới [trên JsFiddle để bạn tham khảo]

const request = window.superagent;
const parser = new RSSParser[];
const CORS_PROXY = "//cors-anywhere.herokuapp.com/";
 
request
  .get["//api.github.com/users/abraham"]
  .set["Accept", "application/json"]
  .end[[err, res] => {
    if [!err] {
      console.log["Twitter Username: ", res.body.twitter_username];
 
      parser.parseURL[
        `${CORS_PROXY}//nitter.net/${res.body.twitter_username}/rss`,
        [err, feed] => {
          if [!err] {
            console.log[
              `The last tweet by ${res.body.twitter_username} is - ${feed.items[0].title}`
            ];
          }
        }
      ];
    }
  }];
 
console.log["This would log first"];

Bạn đang sử dụng Superagent và trình phân tích cú pháp RSS để hoàn thành tác vụ. Superagent và RSS parser đều hỗ trợ callback và Promises API [sắp tới]. Nếu mã này được viết cho phụ trợ với Node. js, chúng tôi sẽ không cần proxy CORS. Nó là cần thiết cho frontend

Mã bắt đầu bằng việc khởi tạo superagent dưới dạng yêu cầu, RSSParser và proxy CORS. Sau đó, yêu cầu đầu tiên được thực hiện cho người dùng của chúng tôi —

first log line
second log line
third log line
5 — đối với API GitHub. Khi kết thúc yêu cầu, một cuộc gọi lại ẩn danh được thực hiện với
first log line
second log line
third log line
6 và
first log line
second log line
third log line
7

Tên người dùng Twitter chỉ được rút ra khỏi kết quả nếu không có lỗi. Trình phân tích cú pháp RSS đưa ra một yêu cầu khác để phân tích cú pháp URL RSS và nhận các tweet. Cuộc gọi này dẫn đến việc thực hiện một cuộc gọi lại khác

Đây là đầu ra của đoạn mã trên

first log line
second log line
third log line
1

Nếu bạn muốn tìm hiểu sâu hơn một chút về cách động cơ V8 xử lý hoạt động không đồng bộ, vui lòng xem bài nói chuyện thú vị 'Giúp tôi bị kẹt trong vòng lặp sự kiện' của Philip Roberts. Anh ấy giải thích vòng lặp sự kiện một cách rõ ràng và ngắn gọn

Tiếp theo chúng ta cùng tìm hiểu về callback hell

Địa ngục gọi lại trong JavaScript

Như đã thấy ở trên, việc viết mã định hướng gọi lại cho các hoạt động không đồng bộ không cảm thấy tự nhiên. Ngoài ra, nếu cần ba lần gọi lại trở lên để hoàn thành một tác vụ, mã của chúng ta sẽ trở nên khó viết, khó hiểu và cuối cùng là quản lý

Vì vậy, khi bạn viết JavaScript theo cách mà quá trình thực thi diễn ra một cách trực quan từ trên xuống dưới với nhiều cấp độ gọi lại phức tạp, bạn sẽ rơi vào địa ngục gọi lại. Một trong những cách dễ nhất để hiểu callback hell là thông qua hình ảnh

Nếu bạn không muốn vào vùng địa ngục gọi lại, bạn có thể sử dụng lời hứa với JavaScript không đồng bộ

Bài viết tiếp tục bên dưới

AppSignal APM cho nút. js

  • Thiết lập đơn giản, thông tin chi tiết vô giá
  • Phát hiện rò rỉ bộ nhớ và truy vấn N+1
  • Giữ lỗi tránh xa người dùng của bạn

Tìm hiểu thêm →

Async JavaScript với Promises

Lời hứa [còn được gọi là tương lai trong các ngôn ngữ khác] là các đối tượng biểu thị sự hoàn thành hoặc thất bại cuối cùng của một tác vụ không đồng bộ, dẫn đến một giá trị

Ok, bạn có thể hiểu nó tốt hơn với một phép loại suy khác

Giả sử bạn của bạn hứa sẽ gặp bạn vào cuối tuần. Rồi thứ bảy đến. Nếu người bạn thực sự gặp bạn, lời hứa được "thực hiện". Nếu bạn của bạn không xuất hiện trong cuộc họp, lời hứa bị "từ chối". Cho đến thứ bảy, lời hứa là "đang chờ xử lý"

Khái niệm tương tự có thể giải thích bất kỳ hoạt động không đồng bộ nào — ví dụ: gọi URL/API bên ngoài hoặc đọc tệp từ đĩa

Khi một API được gọi, đối tượng lời hứa đang chờ xử lý cho đến khi có câu trả lời. Nếu mọi việc suôn sẻ, lời hứa được thực hiện và phương thức

first log line
second log line
third log line
8 nhận được kết quả. Trong trường hợp thất bại, phương thức
first log line
second log line
third log line
9 được gọi, có đối tượng lỗi. Nếu bạn là một người trực quan, sơ đồ này của tài liệu web MDN có thể giúp bạn hiểu ý tưởng này

Tại thời điểm này, bạn sẽ chuyển đổi mã gọi lại ở trên thành lời hứa. Superagent và RSS Parser đều đã cung cấp API dựa trên lời hứa. Mã để gọi API của GitHub, sau đó lấy tên người dùng Twitter của người dùng

first log line
second log line
third log line
5 [một trong những người dùng GitHub phổ biến ở Hoa Kỳ] và gọi Nitter RSS cho tên người dùng sẽ như thế này

first log line
second log line
third log line
5

Nó chủ yếu là cùng một mã từ quan điểm thực thi, nhưng được viết khác nhau. Nó sử dụng lời hứa thay cho cuộc gọi lại ngay bây giờ

Nếu lời hứa không được Superagent và RSS Parser hỗ trợ nguyên bản, thì chúng cũng có thể được viết bằng đối tượng Promise của JavaScript, nhưng bạn không phải tự mình thực hiện. trong nút. js, chúng ta cũng có thể làm điều đó với tiện ích. Chức năng Promisfy — một chủ đề cho một bài viết khác

Có một số điều bạn nên chú ý trong đoạn mã trên. Đầu tiên, để cung cấp tên người dùng Twitter cho các phương thức

first log line
second log line
third log line
8 khác, một biến
console.log["first log line"];
 
// 1 second wait
setTimeout[function [] {
  console.log["third log line - after 1 second"];
}, 1000];
 
console.log["second log line"];
2 được đặt trước
console.log["first log line"];
 
// 1 second wait
setTimeout[function [] {
  console.log["third log line - after 1 second"];
}, 1000];
 
console.log["second log line"];
3 thành một chuỗi trống. Khi giá trị khả dụng, giá trị này được đặt thành giá trị tên người dùng Twitter được sử dụng gần đây nhất vì giá trị này vẫn còn trong phạm vi

Trong trường hợp có lỗi trong phương thức

first log line
second log line
third log line
8, lỗi sẽ được gửi đến phương thức
first log line
second log line
third log line
9, không thực hiện phương thức
first log line
second log line
third log line
8 tiếp theo

Bạn thấy một chuỗi lời hứa ở đây - lời hứa đầu tiên gọi GitHub và lời hứa thứ hai nhận Tweet mới nhất từ ​​​​Nitter trong chuỗi thứ hai. Điều này có thể gây nhầm lẫn. Chúng ta có thể làm điều này rõ ràng hơn với cú pháp async/await — chúng ta sẽ đề cập đến vấn đề này trong phần tiếp theo

Khi đoạn mã trên được thực thi, nó cho kết quả như bên dưới, không khác với đầu ra gọi lại

first log line
second log line
third log line
1

Bạn cũng có thể xem đoạn mã hứa hẹn trên JSFiddle và thử với nó

Nếu lời hứa có vẻ thú vị với bạn, vui lòng tìm hiểu sâu hơn về Lời hứa. tất cả và lời hứa. đua nhau tìm những cách khác để thực hiện lời hứa đồng thời

Bây giờ, hãy tìm hiểu về cách làm việc hiện đại với các lời hứa — sử dụng cú pháp async và await

Không đồng bộ với Đang chờ lời hứa

Không đồng bộ với chờ đợi là cách làm việc hiện đại với những lời hứa theo phong cách rõ ràng hơn nhiều. Async/await có nhiều cú pháp hơn so với lời hứa hơn là một tính năng hoàn toàn mới của ECMAScript

Async cho phép bạn viết mã dựa trên lời hứa như thể nó đồng bộ. Hàm async sẽ luôn trả về một lời hứa, ngầm giúp làm việc với nó dễ dàng hơn. Từ khóa chờ đợi chỉ có thể được sử dụng trong các chức năng không đồng bộ và đợi lời hứa chuyển sang trạng thái hoàn thành. Trạng thái hoàn chỉnh ở đây đề cập đến trạng thái 'đã hoàn thành' hoặc 'bị từ chối'. Cũng có những cuộc thảo luận về sự chờ đợi cấp cao nhất, nhưng nó vẫn chưa trở thành xu hướng chủ đạo.

Với tất cả thông tin đó, bây giờ bạn sẽ chuyển đổi đoạn mã trên bằng lời hứa, sau đó chuyển sang phiên bản chờ async "thoải mái" hơn, như sau

console.log["first log line"];
 
// 1 second wait
setTimeout[function [] {
  console.log["third log line - after 1 second"];
}, 1000];
 
console.log["second log line"];
3

Đoạn mã trên tương tự như đoạn mã hứa hẹn với

console.log["first log line"];
 
// 1 second wait
setTimeout[function [] {
  console.log["third log line - after 1 second"];
}, 1000];
 
console.log["second log line"];
7 và
console.log["first log line"];
 
// 1 second wait
setTimeout[function [] {
  console.log["third log line - after 1 second"];
}, 1000];
 
console.log["second log line"];
8. Sự khác biệt chính ở đây là tất cả logic được bao bọc trong một hàm không đồng bộ có tên là
console.log["first log line"];
 
// 1 second wait
setTimeout[function [] {
  console.log["third log line - after 1 second"];
}, 1000];
 
console.log["second log line"];
9. Các lời hứa được mở ra với một sự chờ đợi, như từ khóa nói, chờ đợi trước khi chuyển sang dòng tiếp theo. Khi các hoạt động không đồng bộ “hoạt động giống như” mã đồng bộ, các giá trị phụ thuộc vào tác vụ không đồng bộ có thể dễ dàng được gán cho các biến như
first log line
second log line
third log line - after 1 second
0. Đó là lý do tại sao try/catch cũng có ý nghĩa hơn ở đây

Trong trường hợp có bất kỳ lỗi nào, vì mã hoạt động giống như mã đồng bộ, nó sẽ bị bắt trong khối bắt. Đối với những lời hứa sử dụng

console.log["first log line"];
 
// 1 second wait
setTimeout[function [] {
  console.log["third log line - after 1 second"];
}, 1000];
 
console.log["second log line"];
7 và
console.log["first log line"];
 
// 1 second wait
setTimeout[function [] {
  console.log["third log line - after 1 second"];
}, 1000];
 
console.log["second log line"];
8, nếu bỏ qua phần
console.log["first log line"];
 
// 1 second wait
setTimeout[function [] {
  console.log["third log line - after 1 second"];
}, 1000];
 
console.log["second log line"];
8, sẽ bị lỗi. Sử dụng async/await làm cho mã có vẻ đồng bộ, điều này tốt. Tuy nhiên, việc lạm dụng async/await sẽ đánh bại sức mạnh của việc sử dụng ngôn ngữ không đồng bộ như JavaScript, nơi có thể thực hiện "nhiều" việc cùng một lúc

Đoạn mã trên đưa ra đầu ra sau, dựa trên tweet cuối cùng của

first log line
second log line
third log line
5

first log line
second log line
third log line
1

Đoạn mã async/await cũng có sẵn trên Jsfiddle để bạn tham khảo

Bạn đã học được ba cách xử lý mã không đồng bộ trong JavaScript. Trong phần sau, bạn sẽ tìm hiểu cách làm cho mã không đồng bộ JavaScript của bạn hiệu quả hơn

Làm cho mã không đồng bộ JavaScript của bạn hiệu quả hơn

Bạn đã thấy ba biến thể của việc thực hiện hai lệnh gọi HTTP, cái này phụ thuộc vào cái kia. Có một số trường hợp cần xem xét để có thể làm cho mã của bạn hiệu quả hơn

Đầu tiên, hãy sử dụng async/await một cách khôn ngoan. Không code JavaScript như PHP, dùng async/await left, right, center. Điều này sẽ chặn vòng lặp sự kiện

Ví dụ: nếu bạn phải gọi API GitHub cho năm tên người dùng, thì không cần phải gọi từng tên một

Nó có thể được thực hiện đồng thời với

first log line
second log line
third log line - after 1 second
5. Với phương pháp này, hãy cẩn thận để bạn không đạt đến giới hạn tốc độ API, vì các cuộc gọi sẽ được thực hiện đồng thời. Dưới đây là một ví dụ nhanh về
first log line
second log line
third log line - after 1 second
5 đang hoạt động

first log line
second log line
third log line
0

Bạn cũng có thể kiểm tra mã trên JSFiddle. Khi nó chạy, nó cho đầu ra sau

first log line
second log line
third log line
1

Câu thần chú chính ở đây là nghĩ về các phần của mã không phụ thuộc vào khối trước đó. Chạy các phần của mã đồng thời, vì vậy các phần này có thể được chia thành các chức năng nhỏ hơn. Công việc được thực hiện nhanh hơn, sử dụng tất cả các tài nguyên có sẵn như CPU ​​và bộ nhớ

Gói [lại

Trong bài đăng này, chúng tôi đã đề cập đến sự khác biệt giữa các mô hình thực thi và mã đồng bộ và không đồng bộ. Sau đó, chúng tôi đã đề cập đến ba cách để xử lý mã không đồng bộ trong JavaScript, sử dụng lệnh gọi lại, lời hứa và async/await [với một ví dụ gọi hai URL]

Cuối cùng, chúng ta đã thấy một ví dụ về cách viết mã không đồng bộ hiệu quả trong Javascript bằng cách sử dụng đồng thời, tách các phần mã độc lập thành các chức năng khác nhau

Mã hóa vui vẻ

P. S. Nếu bạn thích bài đăng này, hãy đăng ký danh sách Phù thủy JavaScript của chúng tôi để tìm hiểu sâu hàng tháng về các mẹo và thủ thuật JavaScript kỳ diệu hơn

JavaScript có thực thi từng dòng không?

JavaScript là ngôn ngữ đơn luồng, mỗi lần chỉ thực thi một lệnh. Nó có mô hình thực thi Đồng bộ, trong đó từng dòng được thực thi theo từng dòng, từ trên xuống dưới .

JS không đồng bộ hay đồng bộ?

Javascript là ngôn ngữ đơn luồng đồng bộ nhưng với sự trợ giúp của vòng lặp sự kiện và lời hứa, JavaScript được sử dụng để lập trình không đồng bộ. Trong bài viết này, chúng ta đã thảo luận về javascript đồng bộ và không đồng bộ với sự trợ giúp của các ví dụ mã hóa.

Làm cách nào để thực thi mã JavaScript không đồng bộ?

3 cách để viết mã không đồng bộ trong JavaScript. Tìm hiểu về Callbacks, Promises và Async/Await trong JavaScript. .
dòng chảy bình thường. Xem xét ví dụ này cho thứ tự thực hiện thông thường. .
gọi lại. Tham gia như một cái búa đập để can thiệp vào luồng điều khiển bằng cách sử dụng các cuộc gọi lại. .
lời hứa. .
Không đồng bộ & Đang chờ

JavaScript không đồng bộ hoạt động như thế nào?

Lập trình không đồng bộ là một kỹ thuật cho phép chương trình của bạn bắt đầu một tác vụ có khả năng chạy lâu và vẫn có thể đáp ứng các sự kiện khác trong khi tác vụ đó chạy, thay vì phải . Khi nhiệm vụ đó đã hoàn thành, chương trình của bạn sẽ hiển thị kết quả. . Once that task has finished, your program is presented with the result.

Chủ Đề