2012-07-17 40 views
6
function indexArticles(callback) { 
    fs.readdir("posts/", function(err, files) { 
    async.map(files, readPost, function(err, markdown) { 
     async.map(markdown, parse, function(err, results) { 
     async.sortBy(results, function(obj, callback) { 
      callback(err, obj.date); 
     }, function(err, sorted) { 
      callback({"articles": sorted.reverse()}); 
     }); 
     }); 
    }); 
    }); 
} 

Tôi đang cố gắng tìm ra cách làm đẹp hơn - như bạn có thể nói tôi đang sử dụng thư viện không đồng bộ của caolan, nhưng tôi không chắc kiểm soát cấu trúc dòng chảy để sử dụng. Nó có vẻ như nếu tôi sử dụng async.waterfall, ví dụ, kết quả trong mã khá nhiều hơn một chút, với mỗi bước phải được bao bọc trong một hàm ẩn danh. Ví dụ: đây chỉ là hai dòng đầu tiên của phiên bản lồng nhau có thác nước:Tái cấu trúc các cuộc gọi lại lồng nhau, node.js, async

function indexArticles(callback) { 
    async.waterfall([ 
    function(callback) { 
     fs.readdir("posts/", function(err, files) { 
     callback(err, files) 
     }) 
    }, 

    function(files, callback) { 
     async.map(files, readPost, function(err, markdown) { 
     callback(err, markdown) 
     }) 
    }]) 
} 

Bạn sẽ cải thiện điều này như thế nào?

Nếu có một cách để áp dụng một phần tranh luận không chỉ từ bên trái, sau đó tôi có thể thấy làm, ví dụ,

function indexArticles(callback) { 
    async.waterfall([ 
    async.apply(fs.readdir, "posts/"), 
    async.apply(async.map, __, readPost), 
    async.apply(async.map, __, parse), 
    // etc... 
    ]) 
} 
+0

Trong khi 'thác nước 'có thể kết thúc với nhiều ký tự hơn, tôi nghĩ nó sẽ kết thúc dễ đọc hơn. Ngoài ra, hãy kiểm tra 'apply' để trợ giúp tất cả các hàm anon đó. –

+0

Bạn có thể xem ví dụ về thác nước mà tôi vừa mới đăng và cho tôi biết liệu tôi có đang làm đúng không? –

Trả lời

6

Đây là một vấn đề thú vị, khi bạn cần để ràng buộc đối số cho cả người bên trái và bên phải của các hàm lặp của bạn, do đó, không phải bind/cũng không phải bindRight (trong đó có một vài triển khai trên StackOverflow) sẽ hoạt động cho bạn. Có một vài lựa chọn cho bạn ở đây:

(1) Thứ nhất, trong async.waterfall ví dụ của bạn, bạn có:

function(callback) { 
    fs.readdir("posts/", function(err, files) { 
    callback(err, files) 
    }) 
} 

mà là giống như:

function(callback) { 
    fs.readdir("posts/", callback) 
} 

Sử dụng Function.bind và phương pháp này , toàn bộ chức năng của bạn indexArticles có thể được viết:

function indexArticles(callback) { 
    async.waterfall([ 
    fs.readdir.bind(this, 'posts/'), 
    function(files, cb) { async.map(files, readPost, cb); }, 
    function(text, cb) { async.map(text, parse, cb); }, 
    function(results, cb) { async.sortBy(results, function(obj, callback) { 
     callback(null, obj.date); 
    }, cb) } 
    ], function(err, sorted) { 
    callback({"articles": sorted.reverse()}); 
    }); 
}; 

Đó là một chút ngắn hơn.

(2) Nếu bạn thực sự muốn tránh các chức năng gói, bạn có thể sử dụng một loại ứng dụng chức năng một phần. Thứ nhất, ở phía trên cùng của tập tin của bạn (hoặc trong một mô-đun, vv), xác định một chức năng gọi là partial:

var partial = function(fn) { 
    var args = Array.prototype.slice.call(arguments, 1); 
    return function() { 
    var currentArg = 0; 
    for(var i = 0; i < args.length && currentArg < arguments.length; i++) { 
     if (args[i] === undefined) 
     args[i] = arguments[currentArg++]; 
    } 
    return fn.apply(this, args); 
    }; 
} 

chức năng này có một chức năng và bất kỳ số lượng đối số, và thay thế undefined giá trị trong các đối số danh sách với các đối số thực tế khi hàm được gọi. Sau đó bạn sẽ sử dụng nó như thế này:

function indexArticles(callback) { 
    async.waterfall([ 
    fs.readdir.bind(this, 'posts/'), 
    partial(async.map, undefined, readPost, undefined), 
    partial(async.map, undefined, parse, undefined), 
    partial(async.sortBy, undefined, function(obj, callback) { 
     callback(null, obj.date); 
    }, undefined) 
    ], function(err, sorted) { 
    callback({"articles": sorted.reverse()}); 
    }); 
} 

Vì vậy, partial(async.map, undefined, readPost, undefined) trả về một chức năng đó, khi được gọi bởi các thư viện Async như fn(files, callback), nó lấp đầy trong files cho undefined đầu tiên, và callback cho phần thứ hai undefined, kết thúc bằng một cuộc gọi đến async.map(files, readPost, callback).

(3) Cũng có phiên bản partial cho Function.prototype tại this StackOverflow answer, cho phép bạn sử dụng cú pháp: async.map.partial(undefined, readPost, undefined); tuy nhiên, tôi có thể khuyên bạn không nên sửa đổi Function.prototype theo cách này và chỉ sử dụng partial làm chức năng.

Cuối cùng, tùy thuộc vào bạn phương pháp nào dễ đọc nhất và dễ bảo trì nhất.

2

Hình như tôi có một số chồng chéo với câu trả lời của Brandon, nhưng đây là quan điểm của tôi:

var async = require("async") 

//dummy function 
function passThrough(arg, callback){ 
    callback(null, arg) 
} 

//your code rewritten to only call the dummy. 
//same structure, didn't want to think about files and markdown 
function indexArticles(callback) { 
    passThrough("posts/", function(err, files) { 
    async.map(files, passThrough, function(err, markdown) { 
     async.map(markdown, passThrough, 
     function(err, results) { 
      async.sortBy(results, function(obj, callback) { 
      callback(err, obj); 
     }, 
     function(err, sorted) { 
      callback({"articles": sorted.reverse()}); 
     }); 
     }); 
    }); 
    }); 
} 
indexArticles(console.log) 

//version of apply that calls 
//fn(arg, arg, appliedArg, apliedArg, callback) 
function coolerApply(fn) { 
    var args = Array.prototype.slice.call(arguments, 1); 
    return function() { 
    var callback = Array.prototype.slice.call(arguments, -1) 
    var otherArgs = Array.prototype.slice.call(arguments, 0, -1) 
    return fn.apply(
     null, otherArgs.concat(args).concat(callback) 
    ); 
    }; 
}; 

//my version of your code that uses coolerAppl 
function indexArticles2(callback){ 
    async.waterfall([ 
    async.apply(passThrough, "posts/"), 
    coolerApply(async.map, passThrough), 
    coolerApply(async.map, passThrough), 
    coolerApply(async.sortBy, function(obj, callback){callback(null,obj)}) 
    ], 
    function(err, sorted){ 
    callback({"articles": sorted.reverse()}) 
    }) 
} 
//does the same thing as indexArticles! 
indexArticles2(console.log) 
1

Dưới đây là những gì tôi đã kết thúc với cho đến nay.

function indexArticles(callback) { 
    var flow = [ 
    async.apply(fs.readdir, "posts/"), 

    function(data, callback) { async.map(data, readPost, callback); }, 

    function sortByDate(parsed, callback) { 
     var iterator = function(obj, callback) { 
     if (obj.date) { callback(null, obj.date); } 
     else { callback("Article has no date.") } 
     } 
     // Note that this sorts in reverse lexicographical order! 
     async.sortBy(parsed, iterator, 
      function(err, sorted) { callback(err, {"articles": sorted.reverse()}); } 
     ); 
    } 
    ]; 

    async.waterfall(flow, async.apply(callback)) 
} 
1

Tôi vừa mới tạo ra một khái niệm trừu tượng đơn giản có tên WAITFOR để gọi chức năng async trong chế độ đồng bộ hóa (dựa trên sợi): https://github.com/luciotato/waitfor

tôi đã không kiểm tra nó với gói async, nhưng nó phải làm việc . Nếu bạn gặp sự cố, hãy liên hệ với tôi.

Sử dụng wait.for và async mã của bạn sẽ là:

var wait = require('waitfor'); 
var async = require('async'); 

function indexArticles(callback) { 
    var files = wait.for(fs.readdir,"posts/"); 
    var markdown = wait.for(async.map, files, readPost); 
    var results = wait.for(async.map, markdown, parse); 
    var sorted = wait.for(async.sortBy, results, function(obj, callback) { 
                callback(null, obj.date); 
               }); 
    callback(null, {"articles": sorted.reverse()}); 
} 

gọi fn (async mode) của bạn:

//execute in a fiber 
wait.launchFiber(indexArticles,function(err,data){ 
     // do something with err,data 
     }); 

gọi fn (sync-mode) của bạn:

//execute in a fiber 
function handleRequest(req,res){ 
    try{ 
     ... 
     data = wait.for(indexArticles); //call indexArticles and wait for results 
     // do something with data 
     res.end(data.toString()); 
    } 
    catch(err){ 
     // handle errors 
    } 
} 

// express framework 
app.get('/posts', function(req, res) { 
    // handle request in a Fiber, keep node spinning 
    wait.launchFiber(handleRequest,req,res); 
    }); 
Các vấn đề liên quan