我有两个我异步运行的功能。我想用瀑布模型来编写它们。问题是,我不知道如何......
这是我的代码:
var fs = require('fs');
function updateJson(ticker, value) {
//var stocksJson = JSON.parse(fs.readFileSync("stocktest.json"));
fs.readFile('stocktest.json', function(error, file) {
var stocksJson = JSON.parse(file);
if (stocksJson[ticker]!=null) {
console.log(ticker+" price : " + stocksJson[ticker].price);
console.log("changing the value...")
stocksJson[ticker].price = value;
console.log("Price after the change has been made -- " + stocksJson[ticker].price);
console.log("printing the the Json.stringify")
console.log(JSON.stringify(stocksJson, null, 4));
fs.writeFile('stocktest.json', JSON.stringify(stocksJson, null, 4), function(err) {
if(!err) {
console.log("File successfully written");
}
if (err) {
console.error(err);
}
}); //end of writeFile
} else {
console.log(ticker + " doesn't exist on the json");
}
});
} // end of updateJson
任何想法我怎么能用瀑布写它,所以我能够控制它?请给我一些例子,因为我是node.js的新手
答案 0 :(得分:56)
阅读文件
function readFile(readFileCallback) {
fs.readFile('stocktest.json', function (error, file) {
if (error) {
readFileCallback(error);
} else {
readFileCallback(null, file);
}
});
}
处理文件(我删除了示例中的大部分console.log)
function processFile(file, processFileCallback) {
var stocksJson = JSON.parse(file);
if (stocksJson[ticker] != null) {
stocksJson[ticker].price = value;
fs.writeFile('stocktest.json', JSON.stringify(stocksJson, null, 4), function (error) {
if (err) {
processFileCallback(error);
} else {
console.log("File successfully written");
processFileCallback(null);
}
});
}
else {
console.log(ticker + " doesn't exist on the json");
processFileCallback(null); //callback should always be called once (and only one time)
}
}
请注意,我没有在这里进行特定的错误处理,我将利用async.waterfall来集中错误处理在同一个地方。
另外要注意,如果你在异步函数中有(if / else / switch / ...)分支,它总是调用回调一次(并且只有一次)。
async.waterfall([
readFile,
processFile
], function (error) {
if (error) {
//handle readFile error or processFile error here
}
});
之前的代码过于冗长,使解释更加清晰。这是一个完整的清理示例:
async.waterfall([
function readFile(readFileCallback) {
fs.readFile('stocktest.json', readFileCallback);
},
function processFile(file, processFileCallback) {
var stocksJson = JSON.parse(file);
if (stocksJson[ticker] != null) {
stocksJson[ticker].price = value;
fs.writeFile('stocktest.json', JSON.stringify(stocksJson, null, 4), function (error) {
if (!err) {
console.log("File successfully written");
}
processFileCallback(err);
});
}
else {
console.log(ticker + " doesn't exist on the json");
processFileCallback(null);
}
}
], function (error) {
if (error) {
//handle readFile error or processFile error here
}
});
我保留了函数名称,因为它有助于提高可读性,并有助于使用chrome debugger等工具进行调试。
如果您使用underscore(on npm),则还可以将第一个函数替换为_.partial(fs.readFile, 'stocktest.json')
答案 1 :(得分:14)
首先,请确保read the documentation regarding async.waterfall
。
现在,关于瀑布控制流程有几个关键部分:
err
),它将短路并立即调用"完成" /"完成&#34 ; /"完成" callback
。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);
});
}
答案 2 :(得分:2)
基本上,需要一些时间来执行的nodejs(以及更常见的javascript)函数(无论是用于I / O还是cpu处理)通常都是异步的,因此事件循环(简化它是一个不断检查任务的循环)要执行)可以调用第一个下面的函数,而不会被阻止响应。如果您熟悉其他语言(如C或Java),您可以将异步函数视为在另一个线程上运行的函数(它在javascript中不一定正确,但程序员不应该关心它)当执行终止时,该线程通知主要的一个(事件循环一)该作业已完成,并且它具有结果。
如上所述,一旦第一个函数结束了它的工作,它必须能够通知它的工作已经完成,并且它会调用你传递给它的回调函数。举个例子:
var callback = function(data,err)
{
if(!err)
{
do something with the received data
}
else
something went wrong
}
asyncFunction1(someparams, callback);
asyncFunction2(someotherparams);
执行流将调用:asyncFunction1,asyncFunction2和下面的每个函数,直到asyncFunction1结束,然后调用作为最后一个参数传递给asyncFunction1的回调函数,如果没有错误发生,则对数据执行某些操作。
因此,要使2个或更多异步函数只在它们结束时一个接一个地执行,你必须在它们的回调函数中调用它们:
function asyncTask1(data, function(result1, err)
{
if(!err)
asyncTask2(data, function(result2, err2)
{
if(!err2)
//call maybe a third async function
else
console.log(err2);
});
else
console.log(err);
});
result1是asyncTask1的返回值,result2是asyncTask2的返回值。您可以通过这种方式嵌套您想要的异步函数。
在您的情况下,如果您想在updateJson()之后调用另一个函数,则必须在此行之后调用它:
console.log("File successfully written");