JavaScript异步编程:异步数据收集的具体方法
2013-08-19 00:00
585 查看
Asyncjs/seriesByHand.js
如你所见,异步版本的代码要比同步版本多很多。如果使用filter、forEach这些同步方法,代码的行数大约只有一半,而且读起来也要容易得多。如果这些漂亮的迭代器存在异步版本该多好啊!使用Async.js就能做到这一点!
何时抛出亦无妨?
大家可能注意到了,在上面那个代码示例中笔者无视了自己在第1.4节中提出的建议:从回调里抛出异常是一种糟糕的设计,尤其在成品环境中。不过,一个简单如斯的示例直接抛出异常则完全没有问题。如果真的遇到代码出错的意外情形,throw会关停代码并提供一个漂亮的堆栈轨迹来解释出错原因。
这里真正的不妥之处在于,同样的错误处理逻辑(即if(err) throw err)重复了多达3次!在4.2.2节,我们会看到Async.js如何帮助减少这种重复。
Async.js的函数式写法
我们想把同步迭代器所使用的filter和forEach方法替换成相应的异步方法。Async.js给了我们两个选择。
async.filter和async.forEach,它们会并行处理给定的数组。
async.filterSeries和async.forEachSeries,它们会顺序处理给定的数组。
并行运行这些异步操作应该会更快,那为什么还要使用序列式方法呢?原因有两个。
前面提到的工作流次序不可预知的问题。我们确实可以先把结果存储成数组,然后再joining(联接)数组来解决这个问题,但这毕竟多了一个步骤。
Node及其他任何应用进程能够同时读取的文件数量有一个上限。如果超过这个上限,操作系统就会报错。如果能顺序读取文件,则无需担心这一限制。
所以现在先搞明白async.forEachSeries再说。下面使用了Async.js的数据收集方法,直接改写了同步版本的代码实现。
Asyncjs/forEachSeries.js
现在我们的代码漂亮地分成了两个部分:任务概貌(表现形式为async.filter调用和async.forEachSeries调用)和实现细节(表现形式为两个迭代器函数和一个完工回调onComplete)。
filter和forEach并不是仅有的与标准函数式迭代方法相对应的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/filterSeries、reject/rejectSeries、detect/detectSeries、some、every等)的迭代器均无法报告错误。
对于非布尔型的所有Async.js迭代器,传递非null/undefined的值作为迭代器回调的首参数将会立即因该错误值而调用完工回调。这正是readAndConcat不用throw也能工作的原因。
Asyncjs/forEachSeries.js
所以,如果callback(err)确实是在readAndConcat中被调用的,则这个err会传递给完工回调(即onComplete)。Async.js只负责保证onComplete只被调用一次,而不管是因首次出错而调用,还是因成功完成所有操作而调用。
Asyncjs/forEachSeries.js
Node的错误处理约定对Async.js数据收集方法而言也许并不理想,但对于Async.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); });
如你所见,异步版本的代码要比同步版本多很多。如果使用filter、forEach这些同步方法,代码的行数大约只有一半,而且读起来也要容易得多。如果这些漂亮的迭代器存在异步版本该多好啊!使用Async.js就能做到这一点!
何时抛出亦无妨?
大家可能注意到了,在上面那个代码示例中笔者无视了自己在第1.4节中提出的建议:从回调里抛出异常是一种糟糕的设计,尤其在成品环境中。不过,一个简单如斯的示例直接抛出异常则完全没有问题。如果真的遇到代码出错的意外情形,throw会关停代码并提供一个漂亮的堆栈轨迹来解释出错原因。
这里真正的不妥之处在于,同样的错误处理逻辑(即if(err) throw err)重复了多达3次!在4.2.2节,我们会看到Async.js如何帮助减少这种重复。
Async.js的函数式写法
我们想把同步迭代器所使用的filter和forEach方法替换成相应的异步方法。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)。
filter和forEach并不是仅有的与标准函数式迭代方法相对应的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/filterSeries、reject/rejectSeries、detect/detectSeries、some、every等)的迭代器均无法报告错误。
对于非布尔型的所有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的所有其他方法而言,遵守这些约定可以让错误干净利落地从各个任务流向完工回调。下一节会看到更多这样的例子。
相关文章推荐
- JavaScript异步编程:异步数据收集的具体方法
- JavaScript异步编程:异步的数据收集方法
- 使用ssm框架 通过ajax异步提交数据具体实现方法
- iOS 多个异步网络请求全部返回后再执行具体逻辑的方法
- 关于web页面缓存问题解决方法,如图片缓存,异步提交数据页面不更新
- ASP模拟POST请求异步提交数据的方法
- 常用的前台数据绑定方法---------【个人收集】
- web 开发中的各种页面的提交请求数据方法汇总(持续更新,内容收集来自各网络)
- 电脑数据恢复方法具体步骤
- 数据字典(选项)管理 的具体使用方法介绍 - 以审批流程类别管理为例(工作流类型管理)
- linux中sed的使用方法具体解释(对行数据的加入、删除等)
- Ajax异步获取html数据中包含js方法无效的解决方法
- 使用ASP.NET AJAX异步调用Web Service和页面中的类方法:服务器端和客户端数据类型的自动转换:复杂类型
- 异步传输数据的另一种方法
- ANDROID笔记:基于handle的异步请求网络数据的一种方法
- 使用getJSON()方法异步加载JSON格式数据
- 利用魔兽世界战网API收集数据,从而起底同账号角色的新方法
- Mybatis中,当插入数据后,返回最新主键id的几种方法,及具体用法
- 在Linux系统下移除文件内的隐私数据的方法和具体步骤
- cacti监控工具之自定数据收集方法