首页 > 编程语言 >JavaScript异步编程:异步的数据收集方法

JavaScript异步编程:异步的数据收集方法

时间:2023-06-21 13:05:26浏览次数:32  
标签:function 异步 编程 err JavaScript js fs async Async


我们先尝试在不借助任何工具函数的情况下来解决这个问题。笔者能想到的最简单的方法是:因前一个readFile的回调运行下一个readFile,同时跟踪记录迄今已触发的回调次数,并最终显示输出。下面是笔者的实现结果。

Asyncjs/seriesByHand.js

var fs = require('fs');
process.chdir('recipes'); // 改变工作目录
var concatenation = '';

fs.readdir('.', function(err, filenames) {
  if (err) throw err;

  function readFileAt(i) {
    var filename = filenames[i];
    fs.stat(filename, function(err, stats) {
      if (err) throw err;
      if (! stats.isFile()) return readFileAt(i + 1);

      fs.readFile(filename, 'utf8', function(err, text) {
        if (err) throw err;
        concatenation += text;
        if (i + 1 === filenames.length) {
          // 所有文件均已读取,可显示输出
          return console.log(concatenation);
        }
        readFileAt(i + 1);
      });
    });
  }
  readFileAt(0);
});

如你所见,异步版本的代码要比同步版本多很多。如果使用filterforEach这些同步方法,代码的行数大约只有一半,而且读起来也要容易得多。如果这些漂亮的迭代器存在异步版本该多好啊!使用Async.js就能做到这一点!

何时抛出亦无妨?

大家可能注意到了,在上面那个代码示例中笔者无视了自己在第1.4节中提出的建议:从回调里抛出异常是一种糟糕的设计,尤其在成品环境中。不过,一个简单如斯的示例直接抛出异常则完全没有问题。如果真的遇到代码出错的意外情形,throw会关停代码并提供一个漂亮的堆栈轨迹来解释出错原因。这里真正的不妥之处在于,同样的错误处理逻辑(即if(err) throw err)重复了多达3次!在4.2.2节,我们会看到Async.js如何帮助减少这种重复。

Async.js的函数式写法

我们想把同步迭代器所使用的filterforEach方法替换成相应的异步方法。Async.js给了我们两个选择。async.filter

async.forEach

  • ,它们会并行处理给定的数组。

async.filterSeries

async.forEachSeries

  • ,它们会顺序处理给定的数组。

并行运行这些异步操作应该会更快,那为什么还要使用序列式方法呢?原因有两个。

  • 前面提到的工作流次序不可预知的问题。我们确实可以先把结果存储成数组,然后再

joining

  • (联接)数组来解决这个问题,但这毕竟多了一个步骤。
  • Node及其他任何应用进程能够同时读取的文件数量有一个上限。如果超过这个上限,操作系统就会报错。如果能顺序读取文件,则无需担心这一限制。

所以现在先搞明白async.forEachSeries再说。下面使用了Async.js的数据收集方法,直接改写了同步版本的代码实现。

Asyncjs/forEachSeries.js

var async = require('async');
var fs = require('fs');
process.chdir('recipes'); // 改变工作目录
var concatenation = '';

var dirContents = fs.readdirSync('.');

async.filter(dirContents, isFilename, function(filenames) {
  async.forEachSeries(filenames, readAndConcat, onComplete);
});

function isFilename(filename, callback) {
  fs.stat(filename, function(err, stats) {
    if (err) throw err;
    callback(stats.isFile());
  });
}

function readAndConcat(filename, callback) {
  fs.readFile(filename, 'utf8', function(err, fileContents) {
    if (err) return callback(err);
    concatenation += fileContents;
    callback();
  });
}

function onComplete(err) {
  if (err) throw err;
  console.log(concatenation);
}

现在我们的代码漂亮地分成了两个部分:任务概貌(表现形式为async.filter调用和async.forEachSeries调用)和实现细节(表现形式为两个迭代器函数和一个完工回调onComplete)。filterforEach并不是仅有的与标准函数式迭代方法相对应的Async.js工具函数。Async.js还提供了以下方法:reject

  • /

rejectSeries

  • ,与

filter

  • 刚好相反;

map

  • /

mapSeries

  • ,1:1变换;

reduce

  • /

reduceRight

  • ,值的逐步变换;

detect

  • /

detectSeries

  • ,找到筛选器匹配的值;

sortBy

  • ,产生一个有序副本;

some

  • ,测试是否至少有一个值符合给定标准;

every

  • ,测试是否所有值均符合给定标准。

这些方法是Async.js的精髓,令你能够以最低的代码重复度来执行常见的迭代工作。在继续探索更高级的方法之前,我们先来看看这些方法的错误处理技术。

Async.js的错误处理技术

要怪就怪Node的fs.exists首开这一先河吧!而这也意味着使用了Async.js数据收集方法(filter/filterSeriesreject/rejectSeriesdetect/detectSeriessomeevery等)的迭代器均无法报告错误。对于非布尔型的所有Async.js迭代器,传递非null/undefined的值作为迭代器回调的首参数将会立即因该错误值而调用完工回调。这正是readAndConcat不用throw也能工作的原因。

Asyncjs/forEachSeries.js

function readAndConcat(filename, callback) {
  fs.readFile(filename, 'utf8', function(err, fileContents) {
    if (err) return callback(err);
    concatenation += fileContents;
    callback();
  });
}

所以,如果callback(err)确实是在readAndConcat中被调用的,则这个err会传递给完工回调(即onComplete)。Async.js只负责保证onComplete只被调用一次,而不管是因首次出错而调用,还是因成功完成所有操作而调用。

Asyncjs/forEachSeries.js

function onComplete(err) {
  if (err) throw err;
  console.log(concatenation);
}

Node的错误处理约定对Async.js数据收集方法而言也许并不理想,但对于Async.js的所有其他方法而言,遵守这些约定可以让错误干净利落地从各个任务流向完工回调。下一节会看到更多这样的例子。

标签:function,异步,编程,err,JavaScript,js,fs,async,Async
From: https://blog.51cto.com/u_15767091/6528188

相关文章

  • JavaScript版本的策略模式
    俗话说,条条大路通罗马。在美剧《越狱》中,主角MichaelScofield就设计了两条越狱的道路。这两条道路都可以到达靠近监狱外墙的医务室。同样,在现实中,很多时候也有多种途径到达同一个目的地。比如我们要去某个地方旅游,可以根据具体的实际情况来选择出行的线路。如果没有时间但是不在乎......
  • JavaScript王国里的鸭子合唱团
    编程语言按照数据类型大体可以分为两类,一类是静态类型语言,另一类是动态类型语言。静态类型语言在编译时便已确定变量的类型,而动态类型语言的变量类型要到程序运行的时候,待变量被赋予某个值之后,才会具有某种类型。静态类型语言的优点首先是在编译时就能发现类型不匹配的错误,编辑......
  • POSTGRESQL 事务控制(三) 事务关闭与怎么设置PG 异步提交提高性能
    。最近接到网友反馈,说次系列是打开5秒钟系列,打开5秒后就关闭了,其实我想说的是,我更难,写的脑袋疼,不过估计疼一段时间就不会再疼的,看一段时间就可以看更长的时间,人的进步是吧。接上期,事务在执行完毕后,结束的动作分为两种,1事务提交2 事务回滚至于事务回滚时的问题主要也分手动终止以......
  • 10个具体项目生动精彩讲述JavaScript;超级Web应用,构建不再困难
     “JavaScript,就是那种小时候长得很丑,长大了却谁都想要的孩子。”诞生初期,由于很多所谓的“资深”程序员的滥用,让这个孩子饱受质疑,直到前几年DOM技术开始崛起,JavaScript才逐渐恢复了曾经的兴盛。并且,这时的JavaScript更多了一份成熟,少了一缕稚气。JavaScript虽然已经被当......
  • 活学活用流行的JavaScript库——《JavaScript实战》
    媒体评论“本书并不像一本教材,畅读之后,你会觉得它就是一位讲课生动的老师,带领你遨游JavaScript海洋,把你推向技术风浪的前沿,同时又给了你一个功能完备的冲浪板,接下来就是你在风口浪尖上享受JavaScript高潮的时候了。”                  ......
  • 信捷PLC程序,八轴程序,有伺服也有步进,内部有伺服和步进计算公式换算,模块化编程框架,包含
    信捷PLC程序,八轴程序,有伺服也有步进,内部有伺服和步进计算公式换算,模块化编程框架,包含各功能区规划,伺服步进电机DOG+JOG,气缸手动,公式计算数据处理,报警功能区,自动步进S调用等。研究透彻应用此思维,完全能应用上手中大型各日系主流系统,如日本三菱,松下,欧姆龙,基恩士,国内主流信捷,汇川,台......
  • Copilot很快将可以实现80%的代码编程
    如果从事后的角度看互联网大厂的收购案例,我觉得这几个可以堪称完美:Google收购YouTube和Android,Facebook收购Instagram,字节收购Musical.ly,以及微软对GitHub的收购。而微软以75亿美金的价格对GitHub的收购在如今的AI时代,显得更加突出,既走在了AI的最前沿,也让Gi......
  • JavaScript(05): 正则表达式
    当检索某个文本的时候,我们可能需要一种模式来描述被检索的内容,在这种情况下就可以考虑使用正则表达式。正则表达式可以定义一种模式,这种模式可能是一个简单的字符、也可能是一个非常复杂的字符串,通常被用于解析、格式检查、替换等。关于正则表达式的具体内容可以参考以下链接,本文只......
  • JavaScript(02): ECMAScript基础
    ECMAScript提供了实现通用程序设计任务必需的JavaScript的语法、运算符和基本对象。1.语法ECMAScript借用了Java、C等语言的语法,对于熟悉这些语言的开发者掌握ECMAScript的语法应该是非常容易的。区分大小写变量是弱类型:定义变量时只用var关键字并且可以将变量初始化为任意值每行......
  • 【数据库原理、编程与性能】The Relational Model
    ChapterTwo-TheRelationalModel文章目录ChapterTwo-TheRelationalModel1.CAP数据库2.RelationalAlgebra2.1集合运算(SetTheoreticOperations)2.1.1交(intersection:)2.1.2并(union:)2.1.3差(difference:)2.1.4笛卡尔积(production:)2.2专门的关系运算(NativeSpecialOperat......