[英]Using Async waterfall in node.js
我有兩個函數,我正在異步運行。 我想用瀑布模型來編寫它們。 問題是,我不知道怎么做..
這是我的代碼:
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的新手
讀取文件
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調試器等工具進行調試。
如果你使用下划線 ( 在npm上 ),你也可以用_.partial(fs.readFile, 'stocktest.json')
替換第一個函數_.partial(fs.readFile, 'stocktest.json')
首先,請務必閱讀有關async.waterfall
的文檔 。
現在,關於瀑布控制流程有幾個關鍵部分:
err
),它將短路並立即調用“完成”/“完成”/“完成” 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);
});
}
基本上,需要一些時間來執行的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的回調函數,以便在沒有錯誤發生時對數據執行某些操作。
因此,要使兩個或更多異步函數只在它們結束時一個接一個地執行,您必須在它們的回調函數中調用它們:
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");
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.