Không có gì thực sự song song trong node.js vì nó là một luồng. Tuy nhiên, nhiều sự kiện có thể được lên lịch và chạy theo một trình tự mà bạn không thể xác định trước. Và một số thứ như truy cập cơ sở dữ liệu thực sự "song song" trong đó bản thân các truy vấn cơ sở dữ liệu được chạy trong các luồng riêng biệt nhưng được tích hợp lại vào luồng sự kiện khi hoàn thành.
Vì vậy, làm cách nào để bạn lập lịch gọi lại trên nhiều trình xử lý sự kiện? Đây là một kỹ thuật phổ biến được sử dụng trong hoạt ảnh trong javascript phía trình duyệt: sử dụng một biến để theo dõi quá trình hoàn thành.
Điều này nghe có vẻ giống như một vụ hack và đúng như vậy, và nó có vẻ lộn xộn khi để lại một loạt các biến toàn cục xung quanh việc theo dõi và nó sẽ là một ngôn ngữ đơn giản hơn. Nhưng trong javascript chúng ta có thể sử dụng các bao đóng:
function fork (async_calls, shared_callback) {
var counter = async_calls.length;
var callback = function () {
counter --;
if (counter == 0) {
shared_callback()
}
}
for (var i=0;i<async_calls.length;i++) {
async_calls[i](callback);
}
}
fork([A,B,C],D);
Trong ví dụ trên, chúng tôi giữ cho mã đơn giản bằng cách giả sử các hàm không đồng bộ và gọi lại không yêu cầu đối số. Tất nhiên, bạn có thể sửa đổi mã để chuyển các đối số cho các hàm không đồng bộ và để hàm gọi lại tích lũy kết quả và chuyển nó đến hàm shared_callback.
Câu trả lời bổ sung:
Trên thực tế, ngay cả như vậy, fork()
hàm đó đã có thể chuyển các đối số cho các hàm không đồng bộ bằng cách sử dụng một bao đóng:
fork([
function(callback){ A(1,2,callback) },
function(callback){ B(1,callback) },
function(callback){ C(1,2,callback) }
],D);
việc duy nhất cần làm là tích lũy kết quả từ A, B, C và chuyển chúng cho D.
Thậm chí nhiều câu trả lời bổ sung:
Tôi không thể cưỡng lại. Hãy nghĩ về điều này trong bữa sáng. Đây là cách triển khai fork()
tích lũy kết quả (thường được truyền dưới dạng đối số cho hàm gọi lại):
function fork (async_calls, shared_callback) {
var counter = async_calls.length;
var all_results = [];
function makeCallback (index) {
return function () {
counter --;
var results = [];
for (var i=0;i<arguments.length;i++) {
results.push(arguments[i]);
}
all_results[index] = results;
if (counter == 0) {
shared_callback(all_results);
}
}
}
for (var i=0;i<async_calls.length;i++) {
async_calls[i](makeCallback(i));
}
}
Đó là đủ dễ dàng. Điều này tạo nên fork()
mục đích khá chung và có thể được sử dụng để đồng bộ hóa nhiều sự kiện không đồng nhất.
Cách sử dụng ví dụ trong Node.js:
function A (c){ fs.readFile('file1',c) };
function B (c){ fs.readFile('file2',c) };
function C (c){ fs.readFile('file3',c) };
function D (result) {
file1data = result[0][1];
file2data = result[1][1];
file3data = result[2][1];
}
fork([A,B,C],D);
Cập nhật
Mã này được viết trước khi có các thư viện như async.js hoặc các thư viện dựa trên hứa hẹn khác nhau. Tôi muốn tin rằng async.js được lấy cảm hứng từ điều này nhưng tôi không có bất kỳ bằng chứng nào về điều đó. Dù sao .. nếu bạn đang nghĩ đến việc này ngày hôm nay, hãy xem async.js hoặc các lời hứa. Chỉ cần coi câu trả lời ở trên là một lời giải thích / minh họa hay về cách những thứ như async.parallel hoạt động.
Vì lợi ích hoàn chỉnh, sau đây là cách bạn thực hiện với async.parallel
:
var async = require('async');
async.parallel([A,B,C],D);
Lưu ý rằng nó async.parallel
hoạt động hoàn toàn giống như fork
chức năng chúng tôi đã triển khai ở trên. Sự khác biệt chính là nó chuyển một lỗi làm đối số đầu tiên D
và gọi lại làm đối số thứ hai theo quy ước node.js.
Sử dụng lời hứa, chúng tôi viết nó như sau:
Promise.all([A,B,C]).then(D);