Kiểm soát luồng không đồng bộ bằng cách sử dụng async

Mã điều khiển cho một số trang LocalLibrary của chúng tôi sẽ phụ thuộc vào kết quả của nhiều yêu cầu bất đồng bộ, có thể được yêu cầu chạy theo một số thứ tự cụ thể hoặc song song. Để quản lý kiểm soát dòng chảy, và đưa ra trang khi chúng tôi có tất cả các thông tin cần thiết có sẵn, chúng tôi sẽ sử dụng các nút phổ biến async module.

Lưu ý: Có một số cách khác để quản lý hành vi bất đồng bộ và kiểm soát luồng trong JavaScript, bao gồm các tính năng ngôn ngữ JavaScript tương đối gần đây như Promises.

Async có rất nhiều phương pháp hữu ích (xem tài liệu ). Một số chức năng quan trọng hơn là:

  • async.parallel() để thực hiện bất kỳ thao tác nào phải được thực hiện song song.
  • async.series() khi chúng ta cần đảm bảo rằng các hoạt động bất đồng bộ được thực hiện theo chuỗi.
  • async.waterfall() cho các hoạt động phải được chạy theo chuỗi, với mỗi thao tác tùy thuộc vào kết quả của các phép toán trước đó.

Tại sao nó cần thiết?

Hầu hết các phương thức chúng tôi sử dụng trong Express đều bất đồng bộ — bạn chỉ định một thao tác để thực hiện, chuyển một cuộc gọi lại. Phương thức trả về ngay lập tức và gọi lại được gọi khi hoạt động được yêu cầu hoàn tất. Theo quy ước trong Express , hàm gọi lại truyền giá trị lỗi làm tham số đầu tiên (hoặc nullthành công) và kết quả từ hàm (nếu có) làm tham số thứ hai.

Nếu một bộ điều khiển chỉ cần thực hiện một thao tác bất đồng bộ để có được thông tin cần thiết để hiển thị một trang thì việc triển khai thực hiện rất dễ dàng — chúng tôi chỉ cần hiển thị mẫu trong cuộc gọi lại. Đoạn mã dưới đây cho thấy điều này cho một hàm làm cho số đếm của một mô hình SomeModel(sử dụng count()phương thức Mongoose ):

exports.some_model_count = function(req, res, next) {
 
  SomeModel.count ({a_model_field: 'match_value'}, hàm (err, count) {
    // ... làm gì đó nếu có lỗi

    // Khi thành công, trả kết quả bằng cách chuyển số đếm vào hàm render (ở đây, dưới dạng biến 'data').
    res.render ('the_template', {data: count});
  });
}

Tuy nhiên, nếu bạn cần thực hiện nhiều truy vấn bất đồng bộ và bạn không thể hiển thị trang cho đến khi tất cả các thao tác đã hoàn tất? Việc triển khai ngây thơ có thể "chuỗi daisy" các yêu cầu, khởi động các yêu cầu tiếp theo trong cuộc gọi lại của yêu cầu trước đó và hiển thị phản hồi trong cuộc gọi lại cuối cùng. Vấn đề với cách tiếp cận này là các yêu cầu của chúng tôi sẽ phải được chạy theo chuỗi, mặc dù nó có thể hiệu quả hơn để chạy chúng song song. Điều này cũng có thể dẫn đến mã lồng nhau phức tạp, thường được gọi là địa chỉ gọi lại (callback hell) .

Một giải pháp tốt hơn sẽ là thực hiện tất cả các yêu cầu song song và sau đó có một cuộc gọi lại duy nhất thực hiện khi tất cả các truy vấn đã hoàn thành. Đây là loại hoạt động lưu lượng mà mô-đun Async làm cho dễ dàng!

Hoạt động bất đồng bộ song song

Phương pháp async.parallel()này được sử dụng để chạy nhiều hoạt động bất đồng bộ song song.

Đối số đầu tiên async.parallel()là một tập hợp các hàm bất đồng bộ để chạy (một mảng, đối tượng hoặc có thể lặp lại khác). Mỗi chức năng được truyền callback(err, result)mà nó phải gọi khi hoàn thành với một lỗi err(có thể là null) và một resultsgiá trị tùy chọn .

Đối số thứ hai tùy chọn  async.parallel()là một cuộc gọi lại sẽ được chạy khi tất cả các hàm trong đối số đầu tiên đã hoàn thành. Gọi lại được gọi với một đối số lỗi và một bộ sưu tập kết quả có chứa các kết quả của các hoạt động bất đồng bộ riêng lẻ. Bộ sưu tập kết quả có cùng kiểu với đối số đầu tiên (tức là nếu bạn truyền một mảng các hàm bất đồng bộ, thì hàm gọi lại cuối cùng sẽ được gọi với một mảng các kết quả). Nếu bất kỳ hàm song song nào báo cáo lỗi thì gọi lại được gọi sớm (với giá trị lỗi).

Ví dụ dưới đây cho thấy cách thức này hoạt động khi chúng ta truyền một đối tượng làm đối số đầu tiên. Như bạn có thể thấy, các kết quả được trả về trong một đối tượng có cùng tên thuộc tính như các hàm ban đầu được truyền vào.

async.parallel({ 
  one: function(callback) { ... },
  two: function(callback) { ... },
  ...
  something_else: function(callback) { ... }
  }, 
  // optional callback
  function(err, results) {
    // 'results' is now equal to: {one: 1, two: 2, ..., something_else: some_value}
  }
);

Nếu bạn thay vì truyền một mảng các hàm như đối số đầu tiên, kết quả sẽ là một mảng (kết quả thứ tự mảng sẽ khớp với thứ tự ban đầu mà các hàm được khai báo — không phải thứ tự mà chúng đã hoàn thành).

Các hoạt động bất đồng bộ trong chuỗi

Phương thức async.series()này được sử dụng để chạy nhiều hoạt động bất đồng bộ theo thứ tự, khi các hàm tiếp theo không phụ thuộc vào đầu ra của các hàm trước đó. Nó cơ bản được khai báo và cư xử theo cùng một cách như async.parallel().

async.series({ 
  one: function(callback) { ... },
  two: function(callback) { ... },
  ...
  something_else: function(callback) { ... }
  }, 
  // optional callback after the last asynchronous function completes.
  function(err, results) {
    // 'results' is now equals to: {one: 1, two: 2, ..., something_else: some_value} 
  }
);

Lưu ý: Đặc tả ngôn ngữ ECMAScript (JavaScript) nói rằng thứ tự liệt kê một đối tượng là không xác định, vì vậy có thể các hàm sẽ không được gọi theo cùng thứ tự như bạn chỉ định chúng trên tất cả các nền tảng. Nếu thứ tự thực sự quan trọng, thì bạn nên chuyển một mảng thay vì một đối tượng, như hình dưới đây.

async.series([
  function(callback) {
    // do some stuff ...
    callback(null, 'one'); 
  },
  function(callback) {
    // do some more stuff ... 
    callback(null, 'two'); 
  } 
 ], 
  // optional callback
  function(err, results) {
  // results is now equal to ['one', 'two'] 
  }
);

Các hoạt động bất đồng bộ phụ thuộc trong chuỗi

Phương thức async.waterfall()này được sử dụng để chạy nhiều hoạt động bất đồng bộ theo thứ tự khi mỗi hoạt động phụ thuộc vào kết quả của hoạt động trước đó.

Hàm gọi lại được gọi bởi mỗi hàm bất đồng bộ chứa nullđối số đầu tiên và kết quả trong các đối số tiếp theo. Mỗi hàm trong chuỗi lấy các đối số kết quả của lần gọi lại trước đó làm tham số đầu tiên, và sau đó là một hàm gọi lại. Khi tất cả các hoạt động được hoàn thành, một cuộc gọi lại cuối cùng được gọi với kết quả của hoạt động cuối cùng. Cách làm việc này là rõ ràng hơn khi bạn xem xét các đoạn mã dưới đây (ví dụ này là từ async tài liệu):

async.waterfall([
  function(callback) {
    callback(null, 'one', 'two'); 
  }, 
  function(arg1, arg2, callback) { 
    // arg1 now equals 'one' and arg2 now equals 'two' 
    callback(null, 'three'); 
  }, 
  function(arg1, callback) {
    // arg1 now equals 'three'
    callback(null, 'done');
  }
], function (err, result) {
  // result now equals 'done'
}
);

Cài đặt bất đồng bộ

Cài đặt mô-đun bất đồng bộ bằng cách sử dụng trình quản lý gói NPM để chúng tôi có thể sử dụng nó trong mã của chúng tôi. Bạn làm điều này theo cách thông thường, bằng cách mở một dấu nhắc trong thư mục gốc của dự án LocalLibrary và nhập vào lệnh sau đây:

npm install async

Bước tiếp theo

Document Tags and Contributors

Những người đóng góp cho trang này: SphinxKnight, nhatphuongb1
Cập nhật lần cuối bởi: SphinxKnight,