首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >在node.js中使用异步瀑布

在node.js中使用异步瀑布
EN

Stack Overflow用户
提问于 2014-09-07 05:46:51
回答 3查看 79.5K关注 0票数 43

我有两个异步运行的函数。我想使用瀑布模型来编写它们。问题是,我不知道怎么..。

下面是我的代码:

代码语言:javascript
运行
复制
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的新手

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2014-09-07 06:24:38

首先确定步骤并将其编写为异步函数(接受回调参数)

  • 读取文件

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 (stocksJsonticker != null) { stocksJsonticker.price = value;fs.writeFile('stocktest.json',JSON.stringify(stocksJson,null,4),function (error) { if (err) { processFileCallback(error);} else {console.log(“文件写入成功”);processFileCallback(null);} });} else { console.log(ticker +“json上不存在”);processFileCallback(null);//回调始终调用一次(且只调用一次)} }

请注意,我在这里没有具体的错误处理,我将利用async.waterfall将错误处理集中在同一位置。

另外要注意,如果您有( if /else/switch/...)在异步函数的分支中,它总是调用回调一次(并且只调用一次)。

使用async.waterfall即插即用

代码语言:javascript
运行
复制
async.waterfall([
    readFile,
    processFile
], function (error) {
    if (error) {
        //handle readFile error or processFile error here
    }
});

干净的例子

前面的代码过于冗长,以使解释更清晰。下面是一个完整的干净示例:

代码语言:javascript
运行
复制
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调试器等工具进行调试。

如果使用underscore (on npm),还可以将第一个函数替换为_.partial(fs.readFile, 'stocktest.json')

票数 63
EN

Stack Overflow用户

发布于 2014-09-07 06:27:12

首先也是最重要的,确保你的read the documentation regarding async.waterfall

现在,关于瀑布控制流有几个关键部分:

_

  1. _

_

  1. _

_

  1. 如果在流数组中的任何操作中遇到错误(通常名为err),则它将短路并立即从先前执行的函数中调用“applied”/“finish”/“done”完成,按顺序依次调用控制流中的下一个函数。

_

  1. _err_applied_

_

  1. _并提供一个“中间”回调作为最后一个参数。注意:第一个函数只有这个“中间”回调,而“完整”回调将具有控制流中最后一个被调用函数的参数(考虑到任何错误),但是对于每个单独的操作(在我的示例中称为appended.
  2. The回调),应该在准备好继续调用时调用err参数,而不是“中间”回调(即cbAsync回调):第一个参数将是错误(如果有错误),第二个参数(第三个、第四个...等)参数将是要传递给后续操作的任何数据。

第一个目标是在引入async.waterfall的同时,让您的代码几乎逐字地工作。我决定删除您的所有console.log语句并简化您的错误处理。这是第一次迭代(未测试的代码):

代码语言:javascript
运行
复制
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.');
        }
    });
}

第二次迭代进一步划分了操作流程。它将其放入更小的面向单一操作的代码块中。我不打算评论它,它不言而喻(同样,未经测试):

代码语言:javascript
运行
复制
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),这也是未经测试的:

代码语言:javascript
运行
复制
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);
    });
}
票数 14
EN

Stack Overflow用户

发布于 2014-09-07 06:41:22

基本上,nodejs (以及更一般的javascript)需要一些时间来执行(无论是I/O还是cpu处理)的函数通常是异步的,因此事件循环(为了简单起见,它是一个不断检查要执行的任务的循环)可以调用第一个函数下面的函数,而不会阻塞响应。如果您熟悉其他语言,如C或Java,您可以认为异步函数是在另一个线程上运行的函数(在javascript中不一定是真的,但程序员不应该关心它),当执行终止时,这个线程通知主线程(事件循环线程)作业已经完成,并且有结果。

如上所述,一旦第一个函数结束了它的作业,它就必须能够通知它的作业已经完成,并且它会调用您传递给它的回调函数。举个例子:

代码语言: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个或更多异步函数仅在它们结束时才能依次执行,您必须在它们的回调函数中调用它们:

代码语言:javascript
运行
复制
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()之后调用另一个函数,则必须在此行之后调用它:

代码语言:javascript
运行
复制
console.log("File successfully written");
票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/25705067

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档