Sự khác biệt giữa lập trình đồng bộ và không đồng bộ (trong node.js) là gì


189

Tôi đã đọc gật đầu và tôi đã bắt gặp hai đoạn mã sau đây.

Cái đầu tiên:

    var result = database.query("SELECT * FROM hugetable");
    console.log("Hello World");

Cái thứ hai:

    database.query("SELECT * FROM hugetable", function(rows) {
       var result = rows;
    });
    console.log("Hello World");

Tôi hiểu những gì họ phải làm, họ truy vấn cơ sở dữ liệu để lấy câu trả lời cho truy vấn. Và sau đó console.log('Hello world').

Mã đầu tiên được cho là mã đồng bộ. Và cái thứ hai là mã không đồng bộ.

Sự khác biệt giữa hai mảnh rất mơ hồ với tôi. Đầu ra sẽ là gì?

Googling về lập trình không đồng bộ cũng không giúp tôi.


41
Stange bạn đã không tìm thấy bất cứ điều gì với google, đó là một chủ đề khá lớn. Trong lập trình đồng bộ, mỗi bước được thực hiện một sau khi bước trước đó được thực hiện xong. Trong asynchroneous, bước 2 sẽ được thực hiện ngay cả khi bước 1 chưa kết thúc. Hàm bạn thấy được xác định trong ví dụ thứ hai của bạn được gọi là hàm callBack và sẽ được chạy ngay khi kết quả từ cơ sở dữ liệu sẽ được trả về, có thể sẽ xuất hiện sau khi console.log được chạy.
Laurent S.

7
@Bartdude Có rất nhiều về lập trình không đồng bộ, nhưng không có lời giải thích nào đơn giản về nó là gì và ý nghĩa của nó trong thực tế.
Azeirah

1
@GabrielLlamas Tại sao chúng ta nên tránh các chức năng đồng bộ?
Charlie Parker

3
@CharlieParker Bởi vì họ chặn vòng lặp sự kiện và bạn sẽ mất tất cả các lợi ích từ mô hình I / O được tổ chức không đồng bộ. Và bởi vì đó là một thực tế xấu. Hãy suy nghĩ về nó theo cách này: Nếu bạn không sử dụng các hàm không đồng bộ, tại sao bạn lại sử dụng Node.js?
Gabriel Llamas

1
@GabrielLlamas, nếu tôi đang thực hiện truy vấn INSERT và tôi muốn sử dụng ID được chèn cuối cùng sau database.query()đó, thì tôi nên gọi nó là đồng bộ, phải không? hoặc những gì nên được tiếp cận? (Câu hỏi này tôi có từ lâu)
San

Câu trả lời:


224

Sự khác biệt là trong ví dụ đầu tiên , chương trình sẽ chặn ở dòng đầu tiên. Dòng tiếp theo (console.log ) sẽ phải chờ.

Trong ví dụ thứ hai , console.logsẽ được thực thi KHI truy vấn đang được xử lý. Nghĩa là, truy vấn sẽ được xử lý ở chế độ nền, trong khi chương trình của bạn đang làm những việc khác và khi dữ liệu truy vấn đã sẵn sàng, bạn sẽ làm bất cứ điều gì bạn muốn với nó.

Vì vậy, một cách ngắn gọn: Ví dụ đầu tiên sẽ chặn, trong khi ví dụ thứ hai sẽ không.

Đầu ra của hai ví dụ sau:

// Example 1 - Synchronous (blocks)
var result = database.query("SELECT * FROM hugetable");
console.log("Query finished");
console.log("Next line");


// Example 2 - Asynchronous (doesn't block) 
database.query("SELECT * FROM hugetable", function(result) {
    console.log("Query finished");
});
console.log("Next line");

Sẽ là:

  1. Query finished
    Next line
  2. Next line
    Query finished

Lưu ý
Trong khi bản thân Node là một luồng đơn , có một số tác vụ có thể chạy song song. Ví dụ, các hoạt động của Hệ thống tệp xảy ra trong một quy trình khác.

Đó là lý do tại sao Node có thể thực hiện các hoạt động không đồng bộ: một luồng đang thực hiện các hoạt động của hệ thống tệp, trong khi luồng Node chính tiếp tục thực thi mã javascript của bạn. Trong một máy chủ điều khiển sự kiện như Node, luồng hệ thống tệp thông báo luồng Node chính của một số sự kiện nhất định như hoàn thành, thất bại hoặc tiến trình, cùng với bất kỳ dữ liệu nào liên quan đến sự kiện đó (chẳng hạn như kết quả của truy vấn cơ sở dữ liệu hoặc lỗi thông báo) và luồng Node chính quyết định phải làm gì với dữ liệu đó.

Bạn có thể đọc thêm về điều này tại đây: Cách mô hình IO không chặn luồng đơn hoạt động trong Node.js


9
Về cơ bản, khi tôi thực thi đoạn mã đầu tiên, nó sẽ làm một cái gì đó như thế này : request query.; 5 seconds later when the request is done; console.log; khi cái thứ hai thực thi : request query; console.log; work on the query;
Azeirah

1
@JohnGalt sql chạy trên một chủ đề khác nhau. Nhưng tất nhiên điều đó phụ thuộc vào việc thực hiện trình điều khiển sql bạn sử dụng. Trình điều khiển sẽ sinh ra một luồng mới, kết nối với mysql và chạy truy vấn. Sau khi hoàn thành, đăng kết quả lên hàng đợi sự kiện và Node sẽ gọi lại.
Salvatorelab

4
Chẳng hạn, ví dụ async có thể xuất ra điều tương tự như # 1 không? Giống như, ví dụ, database.querykết thúc nhanh đến mức khi chúng ta đạt được console.lognhiệm vụ đã hoàn thành.
Greatwolf

2
@TheBronx nếu console.log("Next line");trong ví dụ 2 nằm trong hàm ẩn danh, vì vậy ngay sau console.log("query finished");đó, điều đó có nghĩa là "Dòng tiếp theo" sẽ được in SAU "kết thúc truy vấn" phải không? Vì vậy, nếu tôi có mọi thứ theo kiểu lồng nhau, mọi thứ sẽ chạy theo kiểu đồng bộ, do đó tôi không cần phải lo lắng về việc sử dụng các phiên bản đồng bộ của một số chức năng nhất định. Tôi có đúng theo cách hiểu của tôi không?
Abdul

4
Câu trả lời ngắn gọn : Có @Abdul, bạn đã đúng. Câu trả lời dài : Các hàm lồng nhau (gọi lại) là cách thực hiện tuần tự, "nối tiếp nhau". Nhưng đó không phải là "đồng bộ" về mặt kỹ thuật. Hàm ẩn danh vẫn được thực thi "khi hoạt động chặn kết thúc" hay nói cách khác là "không đồng bộ". Node.js có thể thực thi các chức năng khác trong khi hoạt động chặn đó đang diễn ra. Các chức năng vẫn không đồng bộ, chỉ là bạn đang xâu chuỗi chúng. Đồng bộ hóa chức năng thực thi khối, đó là chìa khóa.
Salvatorelab

74

Sự khác biệt giữa hai cách tiếp cận này như sau:

Cách đồng bộ: Nó chờ cho mỗi thao tác hoàn thành, sau đó chỉ có nó thực hiện thao tác tiếp theo. Đối với truy vấn của bạn: console.log()Lệnh sẽ không được thực thi cho đến khi & trừ khi truy vấn đã thực hiện xong để nhận tất cả kết quả từ Cơ sở dữ liệu.

Cách không đồng bộ: Nó không bao giờ đợi cho mỗi thao tác hoàn thành, thay vào đó, nó chỉ thực hiện tất cả các hoạt động trong GO đầu tiên. Kết quả của mỗi thao tác sẽ được xử lý khi có kết quả. Đối với truy vấn của bạn: console.log()Lệnh sẽ được thực thi ngay sau Database.Query()phương thức. Trong khi truy vấn Cơ sở dữ liệu chạy trong nền và tải kết quả sau khi hoàn tất việc truy xuất dữ liệu.

Trường hợp sử dụng

  1. Nếu các hoạt động của bạn không thực hiện quá nhiều công việc nặng nề như truy vấn dữ liệu khổng lồ từ DB thì hãy tiếp tục với cách Đồng bộ nếu không Cách không đồng bộ.

  2. Theo cách không đồng bộ, bạn có thể hiển thị một số chỉ báo Tiến trình cho người dùng trong khi ở chế độ nền, bạn có thể tiếp tục với công việc nặng của mình. Đây là một kịch bản lý tưởng cho các ứng dụng GUI.


2
Điều đó có nghĩa là db.query (cmd, gọi lại) đang chạy đồng thời (như trong các luồng)? Có phải họ đang chạy cùng một lúc?
Charlie Parker

Trong ví dụ thứ hai của mình, có bất kỳ cơ hội nào mà truy vấn kết thúc nhanh đến mức sau đó nó gọi lại cuộc gọi trước console.logkhông?
Fahmi

@Fahmi về mặt lý thuyết là có, thực tế khá bất khả thi
Leo Messi

24

Điều này sẽ trở nên rõ ràng hơn một chút nếu bạn thêm một dòng vào cả hai ví dụ:

var result = database.query("SELECT * FROM hugetable");
console.log(result.length);
console.log("Hello World");

Cái thứ hai:

database.query("SELECT * FROM hugetable", function(rows) {
   var result = rows;
   console.log(result.length);
});
console.log("Hello World");

Hãy thử chạy chúng và bạn sẽ nhận thấy rằng ví dụ đầu tiên (đồng bộ), result.length sẽ được in ra TRƯỚC dòng 'Hello World'. Trong ví dụ thứ hai (không đồng bộ), result.length sẽ (rất có thể) sẽ được in SAU dòng "Hello World".

Đó là bởi vì trong ví dụ thứ hai, phần database.querynày được chạy không đồng bộ trong nền và tập lệnh tiếp tục ngay lập tức với "Hello World". Chỉ console.log(result.length)được thực hiện khi truy vấn cơ sở dữ liệu đã hoàn thành.


1
bạn nói: result.length (rất có thể) sẽ được in SAU dòng "Hello World". .... tại sao điều đó chỉ "có khả năng nhất"? Tôi nghĩ nó luôn được in sau đầu ra console.log. Cảm ơn đã làm rõ :)
nhân

9
@humanityANDpeace: đó là toàn bộ điểm truy cập không đồng bộ: bạn không biết khi nào nó sẽ được thực hiện. Có lẽ đó là một cơ sở dữ liệu nhanh một cách vô lý và truy vấn cơ sở dữ liệu trả về ngay cả trước khi Javascript đến dòng "Hello World" ...
Martijn

19

Đầu tiên, tôi nhận ra mình đến trễ khi trả lời câu hỏi này.

Trước khi thảo luận về đồng bộ và không đồng bộ, chúng ta hãy xem xét ngắn gọn về cách các chương trình chạy.

Trong trường hợp đồng bộ , mỗi câu lệnh hoàn thành trước khi câu lệnh tiếp theo được chạy. Trong trường hợp này, chương trình được đánh giá chính xác theo thứ tự của các báo cáo.

Đây là cách không đồng bộ hoạt động trong JavaScript. Có hai phần trong công cụ JavaScript, một phần xem mã và xử lý các hoạt động và phần khác xử lý hàng đợi. Việc xử lý hàng đợi xảy ra trong một luồng, đó là lý do tại sao chỉ có một thao tác có thể xảy ra tại một thời điểm.

Khi một hoạt động không đồng bộ (như truy vấn cơ sở dữ liệu thứ hai) được nhìn thấy, mã được phân tích cú pháp và thao tác được đưa vào hàng đợi, nhưng trong trường hợp này, một cuộc gọi lại được đăng ký để chạy khi hoạt động này hoàn thành. Hàng đợi có thể có nhiều hoạt động trong đó. Các hoạt động ở phía trước của hàng đợi được xử lý và loại bỏ khỏi hàng đợi. Khi thao tác truy vấn cơ sở dữ liệu được xử lý, yêu cầu được gửi đến cơ sở dữ liệu và khi hoàn thành việc gọi lại sẽ được thực hiện khi hoàn thành. Tại thời điểm này, bộ xử lý hàng đợi đã "xử lý" thao tác di chuyển sang thao tác tiếp theo - trong trường hợp này

    console.log("Hello World"); 

Truy vấn cơ sở dữ liệu vẫn đang được xử lý, nhưng thao tác console.log nằm ở phía trước hàng đợi và được xử lý. Đây là một hoạt động đồng bộ được thực hiện ngay lập tức dẫn đến kết quả đầu ra "Hello World". Một thời gian sau, hoạt động cơ sở dữ liệu hoàn tất, chỉ sau đó cuộc gọi lại được đăng ký với truy vấn được gọi và xử lý, đặt giá trị của kết quả biến thành các hàng.

Có thể một hoạt động không đồng bộ sẽ dẫn đến một hoạt động không đồng bộ khác, hoạt động thứ hai này sẽ được đưa vào hàng đợi và khi đến trước hàng đợi, nó sẽ được xử lý. Gọi lại cuộc gọi được đăng ký với một hoạt động không đồng bộ là cách thời gian chạy JavaScript trả về kết quả của hoạt động khi nó được thực hiện.

Một phương pháp đơn giản để biết thao tác JavaScript nào không đồng bộ là cần lưu ý nếu nó yêu cầu gọi lại - cuộc gọi lại là mã sẽ được thực thi khi hoạt động đầu tiên hoàn tất. Trong hai ví dụ trong câu hỏi, chúng ta chỉ có thể thấy trường hợp thứ hai có cuộc gọi lại, vì vậy đó là hoạt động không đồng bộ của hai trường hợp. Không phải lúc nào cũng như vậy vì các cách xử lý khác nhau đối với kết quả của một hoạt động không đồng bộ.

Để tìm hiểu thêm, đọc về lời hứa. Hứa hẹn là một cách khác trong đó kết quả của một hoạt động không đồng bộ có thể được xử lý. Điều hay ho về những lời hứa là phong cách mã hóa cho cảm giác giống như mã đồng bộ hơn.

Nhiều thư viện như nút 'fs', cung cấp cả kiểu đồng bộ và kiểu không đồng bộ cho một số thao tác. Trong trường hợp thao tác không mất nhiều thời gian và không được sử dụng nhiều - như trong trường hợp đọc tệp cấu hình - thao tác kiểu đồng bộ sẽ dẫn đến mã dễ đọc hơn.


6

Trong trường hợp đồng bộ, lệnh console.log không được thực thi cho đến khi truy vấn SQL kết thúc.

Trong trường hợp không đồng bộ, lệnh console.log sẽ được thực thi trực tiếp. Kết quả của truy vấn sau đó sẽ được lưu trữ bởi chức năng "gọi lại" sau đó.


1
Nhưng có thực sự được gọi là đồng thời? Điều làm tôi bối rối là, trong mã không đồng bộ, mã thực tế có được chạy song song không?
Charlie Parker

Điều này phụ thuộc vào bộ xử lý (có phải là đa lõi không?) Và hệ điều hành. Xem en.wikipedia.org/wiki/Multithreading_(software)#Multithreading
liên quan đến

4

Sự khác biệt chính là với lập trình không đồng bộ, bạn không dừng thực thi. Bạn có thể tiếp tục thực thi mã khác trong khi 'yêu cầu' đang được thực hiện.


2

Hàm làm cho cái thứ hai không đồng bộ.

Cái đầu tiên buộc chương trình phải đợi cho mỗi dòng kết thúc, nó chạy trước khi dòng tiếp theo có thể tiếp tục. Cái thứ hai cho phép mỗi dòng chạy cùng nhau (và độc lập) cùng một lúc.

Các ngôn ngữ và khung (js, node.js) cho phép không đồng bộ hoặc đồng thời là tuyệt vời cho những thứ yêu cầu truyền thời gian thực (ví dụ: trò chuyện, ứng dụng chứng khoán).


0

Lập trình đồng bộ hóa

Các ngôn ngữ lập trình như C, C #, Java là lập trình đồng bộ, những gì bạn viết sẽ được thực hiện theo thứ tự viết của bạn.

-GET DATA FROM SQL.
//Suppose fetching data take 500 msec

-PERFORM SOME OTHER FUNCTION.
//Performing some function other will take 100 msec, but execution of other 
//task start only when fetching of sql data done (i.e some other function 
//can execute only after first in process job finishes).

-TOTAL TIME OF EXECUTION IS ALWAYS GREATER THAN (500 + 100 + processing time) 
msec

Không đồng bộ

Các NodeJ xuất hiện với tính năng không đồng bộ, về bản chất nó không chặn, giả sử trong bất kỳ tác vụ I / O nào đang làm mất thời gian (tìm nạp, viết, đọc), nodejs sẽ không giữ yên và chờ tác vụ kết thúc, nó ' sẽ bắt đầu thực hiện các tác vụ tiếp theo trong hàng đợi và bất cứ khi nào nhiệm vụ đó hoàn thành, nó sẽ thông báo bằng cách sử dụng gọi lại. Ví dụ sau sẽ giúp:

//Nodejs uses callback pattern to describe functions.
//Please read callback pattern to understand this example

//Suppose following function (I/O involved) took 500 msec
function timeConsumingFunction(params, callback){
  //GET DATA FROM SQL
  getDataFromSql(params, function(error, results){
    if(error){
      callback(error);
    }
    else{
      callback(null, results);
    }
  })
}

//Suppose following function is non-blocking and took 100 msec
function someOtherTask(){
  //some other task
  console.log('Some Task 1');
  console.log('Some Task 2');
}

console.log('Execution Start');

//Start With this function
timeConsumingFunction(params, function(error, results){
    if(error){
      console.log('Error')
    }
    else{
      console.log('Successfull'); 
    }
  })

//As (suppose) timeConsumingFunction took 500 msec, 
//As NodeJs is non-blocking, rather than remain idle for 500 msec, it will start 
//execute following function immediately
someOtherTask();

Trong ngắn hạn, đầu ra là:

Execution Start
//Roughly after 105 msec (5 msec it'll take in processing)
Some Task 1
Some Task 2
//Roughly After 510 msec
Error/Successful //depends on success and failure of DB function execution

Sự khác biệt là rõ ràng khi đồng bộ hóa chắc chắn sẽ mất hơn 600 (500 + 100 + thời gian xử lý) msec, async giúp tiết kiệm thời gian.


0

Các chức năng đồng bộ đang chặn trong khi các chức năng không đồng bộ thì không. Trong các chức năng đồng bộ, các câu lệnh hoàn thành trước khi câu lệnh tiếp theo được chạy. Trong trường hợp này, chương trình được đánh giá chính xác theo thứ tự các câu lệnh và việc thực thi chương trình bị tạm dừng nếu một trong các câu lệnh mất một thời gian rất dài.

Các hàm không đồng bộ thường chấp nhận gọi lại dưới dạng tham số và thực thi tiếp tục trên dòng tiếp theo ngay sau khi hàm không đồng bộ được gọi. Cuộc gọi lại chỉ được gọi khi hoạt động không đồng bộ hoàn tất và ngăn xếp cuộc gọi trống. Các hoạt động nặng như tải dữ liệu từ máy chủ web hoặc truy vấn cơ sở dữ liệu nên được thực hiện không đồng bộ để luồng chính có thể tiếp tục thực hiện các hoạt động khác thay vì chặn cho đến khi hoàn thành thao tác dài đó (trong trường hợp trình duyệt, UI sẽ đóng băng) .

Orginal Đăng trên Github: Liên kết


0

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

Đồng bộ

  • Dừng thực thi mã tiếp theo cho đến khi điều này được thực hiện.
  • Bởi vì đây là điểm dừng của việc thực thi thêm, mã đồng bộ được gọi là 'chặn'. Chặn theo nghĩa là sẽ không có mã nào khác được thực thi.

Không đồng bộ

  • Việc thực thi điều này được hoãn lại trong vòng lặp sự kiện, đây là một cấu trúc trong một máy ảo JS thực thi các hàm không đồng bộ (sau khi ngăn xếp các hàm đồng bộ trống).
  • Mã không đồng bộ được gọi là không chặn vì nó không chặn mã tiếp tục chạy.

Thí dụ:

// This function is synchronous
function log(arg) {
    console.log(arg)
}

log(1);

// This function is asynchronous
setTimeout(() => {
    console.log(2)
}, 0);

log(3)

  • Ví dụ ghi nhật ký 1, 3, 2.
  • 2 được ghi lại sau cùng bởi vì nó nằm trong hàm không đồng bộ được thực thi sau khi ngăn xếp trống.
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.