2009-11-27 43 views
19

Trong Nodej, hầu như không có hoạt động I/O chặn nào. Điều này có nghĩa rằng hầu như tất cả các mã nodejs IO liên quan đến nhiều callback. Này áp dụng cho việc đọc và viết đến/từ cơ sở dữ liệu, tập tin, quy trình, vv Một ví dụ điển hình của việc này là như sau:Quản lý nhiều đệ quy gọi lại trong Nodejs

var useFile = function(filename,callback){ 
    posix.stat(filename).addCallback(function (stats) { 
     posix.open(filename, process.O_RDONLY, 0666).addCallback(function (fd) { 
      posix.read(fd, stats.size, 0).addCallback(function(contents){ 
       callback(contents); 
      }); 
     }); 
    }); 
}; 

... 

useFile("test.data",function(data){ 
    // use data.. 
}); 

Tôi dự đoán viết code mà sẽ làm cho nhiều hoạt động IO, vì vậy tôi mong đợi viết nhiều cuộc gọi lại. Tôi khá thoải mái với việc sử dụng gọi lại, nhưng tôi lo lắng về tất cả các đệ quy. Tôi có nguy cơ chạy vào quá nhiều đệ quy và thổi qua một chồng ở đâu đó không? Nếu tôi làm cho hàng ngàn cá nhân ghi vào kho khóa-giá trị của tôi với hàng nghìn cuộc gọi lại, thì chương trình của tôi có bị sụp đổ không?

Tôi có hiểu lầm hoặc đánh giá thấp tác động không? Nếu không, có cách nào để giải quyết vấn đề này trong khi vẫn sử dụng kiểu mã hóa gọi lại của Nodejs không?

+0

Chủ đề cũ nhưng hãy kiểm tra không đồng bộ: https://github.com/caolan/async – fider

Trả lời

24

Không có mã nào bạn hiển thị đang sử dụng đệ quy. Khi bạn gọi tới số useFile, số này gọi là posix.stat(), trả về và useFile chấm dứt khi nó đã chạy đến khi hoàn thành. Sau đó, khi cuộc gọi đến posix.stat() đã hoàn thành trong hệ thống cơ bản và kết quả có sẵn, chức năng gọi lại mà bạn đã thêm cho điều đó sẽ được thực hiện. Điều đó gọi posix.open(), và sau đó chấm dứt khi nó đã chạy để hoàn thành. Khi tệp đã được mở thành công, chức năng gọi lại cho rằng sẽ thực thi, gọi posix.read() và sau đó sẽ kết thúc khi tệp đã chạy xong. Cuối cùng, khi kết quả đọc có sẵn, hàm bên trong sẽ được thực hiện.

Điểm quan trọng là mỗi chức năng chạy để hoàn thành, vì các cuộc gọi đến các chức năng posix.*() không bị chặn: tức là, chúng quay trở lại ngay lập tức, gây ra một số ma thuật được khởi động trong hệ thống cơ bản. Vì vậy, mỗi hàm của bạn kết thúc, và sau đó một sự kiện sẽ làm cho hàm tiếp theo thực thi; nhưng không có điểm nào là có bất kỳ đệ quy nào.

Cấu trúc lồng nhau của mã có thể cho một ấn tượng rằng nội dung bên trong sẽ phải hoàn thành trước khi nội dung bên ngoài có thể đến điểm cuối của riêng nó. Nhưng trong phong cách lập trình theo hướng sự kiện không đồng bộ này, điều này có ý nghĩa hơn khi thấy sự lồng nhau theo điều kiện của sâu hơn => xảy ra-sau-hơn.

EDIT: Thử thêm một số câu lệnh ghi nhật ký ngay trước khi kết thúc mỗi hàm lồng nhau; điều này sẽ giúp minh họa rằng thứ tự mà chúng hoàn thành là từ bên ngoài vào trong.

+0

Cảm ơn bạn đã xóa hết những suy nghĩ lẫn lộn về vấn đề này. Tôi vẫn cần phải làm việc với nodej nhiều hơn một chút và thực hiện một số thử nghiệm khác (chẳng hạn như ghi nhật ký mà bạn đề xuất) để thuyết phục bản thân rằng tôi không xây dựng một dãy núi hoặc bất cứ thứ gì như thế, nhưng đây là một bước trong đó phương hướng. Cảm ơn một lần nữa – Maciek

0

Giống như bất kỳ JavaScript nào, có thể thực hiện các cuộc gọi đệ quy bằng Node.js. Nếu bạn chạy vào các vấn đề sâu đệ quy (như NickFitz chỉ ra, bạn dường như không có nguy cơ đó), bạn thường có thể viết lại mã của bạn để sử dụng một bộ đếm thời gian thay thế.

1

Công cụ của bạn là tốt. Tôi làm cuộc gọi đệ quy trong Express để theo Chuyển hướng HTTP, nhưng những gì bạn làm là "traversal" và không đệ quy

3

Cùng Ví dụ, với sản lượng debug thêm (xem dưới đây để biết đầu ra):

usefile.js:

var sys = require("sys"), 
    posix = require("posix"); 

var useFile = function(filename,callback){ 
    posix.stat(filename).addCallback(function (stats) { 
     posix.open(filename, process.O_RDONLY, 0666).addCallback(function (fd) { 
      posix.read(fd, stats.size, 0).addCallback(function(contents){ 
       callback(contents); 
       sys.debug("useFile callback returned"); 
      }); 
      sys.debug("read returned"); 
     }); 
     sys.debug("open returned"); 
    }); 
    sys.debug("stat returned"); 
}; 

useFile("usefile.js",function(){}); 

Output:

DEBUG: stat returned 
DEBUG: open returned 
DEBUG: read returned 
DEBUG: useFile callback returned 
3

Bạn có thể thử

http://github.com/creationix/do

hoặc cuộn của riêng bạn như tôi đã làm. Đừng bận tâm thiếu xử lý cho bây giờ lỗi (chỉ cần bỏ qua đó);)

var sys = require('sys'); 

var Simplifier = exports.Simplifier = function() {} 

Simplifier.prototype.execute = function(context, functions, finalFunction) { 
    this.functions = functions; 
    this.results = {}; 
    this.finalFunction = finalFunction; 
    this.totalNumberOfCallbacks = 0 
    this.context = context; 
    var self = this; 

    functions.forEach(function(f) { 
    f(function() { 
     self.totalNumberOfCallbacks = self.totalNumberOfCallbacks + 1; 
     self.results[f] = Array.prototype.slice.call(arguments, 0);  
     if(self.totalNumberOfCallbacks >= self.functions.length) { 
     // Order the results by the calling order of the functions 
     var finalResults = []; 
     self.functions.forEach(function(f) { 
      finalResults.push(self.results[f][0]); 
     }) 
     // Call the final function passing back all the collected results in the right order 
     finalFunction.apply(self.context, finalResults); 
     } 
    }); 
    }); 
} 

Và một ví dụ đơn giản sử dụng nó

// Execute 
new simplifier.Simplifier().execute(
    // Context of execution 
    self, 
    // Array of processes to execute before doing final handling 
    [function(callback) { 
     db.collection('githubusers', function(err, collection) { 
     collection.find({}, {limit:30}, function(err, cursor) { 
      cursor.toArray(function(err, users) { callback(users); }) 
     }); 
     });  
    }, 

    function(callback) { 
     db.collection('githubprojects', function(err, collection) { 
     collection.find({}, {limit:45, sort:[['watchers', -1]]}, function(err, cursor) { 
      cursor.toArray(function(err, projects) { callback(projects); }) 
     }); 
     });    
    } 
    ], 
    // Handle the final result 
    function(users, projects) { 
    // Do something when ready 
    } 
); 
1

Ngoài ra hãy xem tại 'bước' (http://github.com/creationix/step) hoặc 'dòng chảy-js 'trên github. Điều này cho phép bạn viết luồng gọi lại theo kiểu tự nhiên hơn. Điều này cũng sẽ làm cho nó rõ ràng rằng không có đệ quy nào xảy ra.

Các vấn đề liên quan