首先,确保您read the documentation regarding async.waterfall。
现在,关于瀑布控制流有几个关键部分:
- 控制流由一组函数指定为第一个参数调用,当流完成时的“完成”回调作为第二个参数。
- 函数数组在系列中调用(相对于并行)。
- 如果流数组中的任何操作遇到错误(通常命名为
err),它将短路并立即调用“完成”/“完成”/“完成”callback。
- 先前执行的函数的参数是applied 到控制流中的下一个函数,按顺序,“中间”回调作为最后一个参数提供。注意:第一个函数只有这个“中间”回调,而“完成”回调将有控制流中最后一个调用函数的参数(考虑到任何错误),但前面有一个
err 参数而不是一个附加的“中间”回调。
- 每个单独操作的回调(在我的示例中我称之为
cbAsync)应该在您准备好继续前进时调用:第一个参数将是一个错误,如果有的话,第二个(第三个,第四个...等)参数将是您要传递给后续操作的任何数据。
第一个目标是在引入async.waterfall 的同时让您的代码几乎逐字运行。我决定删除您所有的 console.log 语句并简化您的错误处理。这是第一次迭代(未经测试的代码):
var fs = require('fs'),
async = require('async');
function updateJson(ticker,value) {
async.waterfall([ // the series operation list of `async.waterfall`
// waterfall operation 1, invoke cbAsync when done
function getTicker(cbAsync) {
fs.readFile('stocktest.json',function(err,file) {
if ( err ) {
// if there was an error, let async know and bail
cbAsync(err);
return; // bail
}
var stocksJson = JSON.parse(file);
if ( stocksJson[ticker] === null ) {
// if we don't have the ticker, let "complete" know and bail
cbAsync(new Error('Missing ticker property in JSON.'));
return; // bail
}
stocksJson[ticker] = value;
// err = null (no error), jsonString = JSON.stringify(...)
cbAsync(null,JSON.stringify(stocksJson,null,4));
});
},
function writeTicker(jsonString,cbAsync) {
fs.writeFile('stocktest.json',jsonString,function(err) {
cbAsync(err); // err will be null if the operation was successful
});
}
],function asyncComplete(err) { // the "complete" callback of `async.waterfall`
if ( err ) { // there was an error with either `getTicker` or `writeTicker`
console.warn('Error updating stock ticker JSON.',err);
} else {
console.info('Successfully completed operation.');
}
});
}
第二次迭代进一步划分了操作流程。它将其放入较小的面向单一操作的代码块中。我不打算评论它,它不言自明(再次,未经测试):
var fs = require('fs'),
async = require('async');
function updateJson(ticker,value,callback) { // introduced a main callback
var stockTestFile = 'stocktest.json';
async.waterfall([
function getTicker(cbAsync) {
fs.readFile(stockTestFile,function(err,file) {
cbAsync(err,file);
});
},
function parseAndPrepareStockTicker(file,cbAsync) {
var stocksJson = JSON.parse(file);
if ( stocksJson[ticker] === null ) {
cbAsync(new Error('Missing ticker property in JSON.'));
return;
}
stocksJson[ticker] = value;
cbAsync(null,JSON.stringify(stocksJson,null,4));
},
function writeTicker(jsonString,cbAsync) {
fs.writeFile('stocktest.json',jsonString,,function(err) {
cbAsync(err);
});
}
],function asyncComplete(err) {
if ( err ) {
console.warn('Error updating stock ticker JSON.',err);
}
callback(err);
});
}
最后一次迭代通过使用一些 bind 技巧来减少调用堆栈并提高可读性 (IMO),从而缩短了很多时间,同样未经测试:
var fs = require('fs'),
async = require('async');
function updateJson(ticker,value,callback) {
var stockTestFile = 'stocktest.json';
async.waterfall([
fs.readFile.bind(fs,stockTestFile),
function parseStockTicker(file,cbAsync) {
var stocksJson = JSON.parse(file);
if ( stocksJson[ticker] === null ) {
cbAsync(new Error('Missing ticker property in JSON.'));
return;
}
cbAsync(null,stocksJson);
},
function prepareStockTicker(stocksJson,cbAsync) {
stocksJson[ticker] = value;
cbAsync(null,JSON.stringify(stocksJson,null,4));
},
fs.writeFile.bind(fs,stockTestFile)
],function asyncComplete(err) {
if ( err ) {
console.warn('Error updating stock ticker JSON.',err);
}
callback(err);
});
}