nodejs Async详解之三:集合操作

admin
   2014-02-07 11:10:32发布 0收藏, 3049浏览
WebAPP技术
0

<div class=story-content>
                        <p>Async提供了很多针对集合的函数,可以简化我们对集合进行异步操作时的步骤。如下:</p>
<ol>
<li>forEach:对集合中每个元素进行异步操作</li>
<li>map:对集合中的每个元素通过异步操作得到另一个值,得到新的集合</li>
<li>filter:对集合中元素使用异步操作进行筛选,得到符合条件的集合</li>
<li>reject:与filter相似,只是判断条件时正好相反,得到剩下的元素的集合</li>
<li>reduce:使用一个初始值同集合中每一个元素进行异步操作,最后得到一个唯一的结果</li>
<li>detect:得到集合中满足条件的第一个数据</li>
<li>sortBy:对集合中的数据进行异步操作,再根据值从小到大排序</li>
<li>some/any:集合中是否有至少一个元素满足条件</li>
<li>every/all:集合中是否每个元素都满足条件</li>
<li>concat:对集合中的元素进行异步操作,将结果集合并成一个数组</li>
</ol>
<p>下面一一解释:</p>
<h2>1. forEach(arr, iterator(item, callback), callback(err))</h2>
<p>如果想对同一个集合中的所有元素都执行同一个异步操作,可以利用forEach函数。注意该函数将重点放在“执行过程”上,忽略运行后产生的数据。如果需要结果,可使用map函数。</p>
<p>根据执行的方式不同,forEach提供了三个版本:</p>
<ol>
<li>集合中所有元素并行执行</li>
<li>一个一个顺序执行</li>
<li>分批执行,同一批内并行,批与批之间按顺序</li>
</ol>
<p>首先看并行执行的例子,它比较简单,只是打印出传入的元素内容:</p>
<blockquote><p>var arr = [{name:'Jack', delay: 200},     <br />           {name:'Mike', delay: 100},      <br />           {name:'Freewind', delay: 300}];</p>
<p> </p>
<p>async.forEach(arr, function(item, callback) {     <br />    log(’1.1 enter: ‘ + item.name);      <br />    setTimeout(function(){      <br />        log(’1.1 handle: ‘ + item.name);      <br />        callback();      <br />    }, item.delay);      <br />}, function(err) {      <br />    log(’1.1 err: ‘ + err);      <br />});</p>
<p> </p>
</blockquote>
<p>它将打出如下结果:</p>
<blockquote><p>42.244&gt; 1.1 enter: Jack     <br />42.245&gt; 1.1 enter: Mike      <br />42.245&gt; 1.1 enter: Freewind      <br />42.350&gt; 1.1 handle: Mike      <br />42.445&gt; 1.1 handle: Jack      <br />42.554&gt; 1.1 handle: Freewind      <br />42.554&gt; 1.1 err: undefined</p>
</blockquote>
<p>最前面的数据是当前的时间值(秒.毫秒),从中可以看到各异步操作是并行执行的。</p>
<p>如果想同步执行,需要使用forEachSeries函数,它与forEach的用法一模一样,只是执行时是一个一个来的。这里就不给例子了。</p>
<p>当集合中元素很多,既不想一次全部并行操作,又不想一个一个按顺序来,可以使用forEachLimit函数。它可以设定一批处理几个,每一批内并行执行,批与批之间顺序执行。</p>
<blockquote><p>async.forEachLimit(arr, 2, function(item, callback) {     <br />    log(’1.5 enter: ‘ + item.name);      <br />    setTimeout(function(){      <br />        log(’1.5 handle: ‘ + item.name);      <br />        callback(null, item.name);      <br />    }, item.delay);      <br />}, function(err) {      <br />    log(’1.5 err: ‘ + err);      <br />});</p>
<p> </p>
</blockquote>
<p>打印结果如下:</p>
<blockquote><p>42.247&gt; 1.5 enter: Jack     <br />42.248&gt; 1.5 enter: Mike      <br />42.351&gt; 1.5 handle: Mike      <br />42.352&gt; 1.5 enter: Freewind      <br />42.461&gt; 1.5 handle: Jack      <br />42.664&gt; 1.5 handle: Freewind      <br />42.664&gt; 1.5 err: undefined</p>
</blockquote>
<p>可以看到前两个是同时开始的,而第三个是等前两个都完成以后才开始的。</p>
<p>更多详细示例:<a href=https://github.com/freewind/async_demo/blob/master/forEach.js>https://github.com/freewind/async_demo/blob/master/forEach.js</a></p>
<h2>2. map(arr, iterator(item, callback), callback(err, results))</h2>
<p>map的重点是转换,即把集合中的元素通过异步操作转为另一个对象,最后可以得到转换后的对象数组。它也提供了并行与顺序执行两种方式。</p>
<p>这里给一个示例,给集合中的每个元素以异步方式增加!!!:</p>
<blockquote><p>var arr = [{name:'Jack', delay:200}, {name:'Mike', delay: 100}, {name:'Freewind', delay:300}, {name:'Test', delay: 50}];</p>
<p>async.map(arr, function(item, callback) {     <br />    log(’1.1 enter: ‘ + item.name);      <br />    setTimeout(function() {      <br />        log(’1.1 handle: ‘ + item.name);      <br />        callback(null, item.name+’!!!’);      <br />    }, item.delay);      <br />}, function(err,results) {      <br />    log(’1.1 err: ‘, err);      <br />    log(’1.1 results: ‘, results);      <br />});</p>
<p> </p>
</blockquote>
<p>打印结果如下: </p>
<blockquote><p>54.569&gt; 1.1 enter: Jack     <br />54.569&gt; 1.1 enter: Mike      <br />54.569&gt; 1.1 enter: Freewind      <br />54.569&gt; 1.1 enter: Test      <br />54.629&gt; 1.1 handle: Test      <br />54.679&gt; 1.1 handle: Mike      <br />54.789&gt; 1.1 handle: Jack      <br />54.879&gt; 1.1 handle: Freewind      <br />54.879&gt; 1.1 err:      <br />54.879&gt; 1.1 results: [ 'Jack!!!', 'Mike!!!', 'Freewind!!!', 'Test!!!' ]</p>
</blockquote>
<p>可以看到,对各元素的操作是并行的,结果会汇总在一起交给最后的回调。</p>
<p>如果想顺序执行,可使用mapSeries,它与map的用法一模一样。</p>
<p>更多详细示例:<a href=https://github.com/freewind/async_demo/blob/master/map.js>https://github.com/freewind/async_demo/blob/master/map.js</a></p>
<h2>3. filter(arr, iterator(item, callback(test)), callback(results))</h2>
<p>使用异步操作对集合中的元素进行筛选。需要注意的是,iterator的callback只有一个参数,只能接收true或false。</p>
<p>对于出错,该函数没有做出任何处理,直接由nodejs抛出。所以需要注意对Error的处理。</p>
<p>提供了并行与顺序执行两种方式。</p>
<p>并行示例,找到所有&gt;=3的元素:</p>
<blockquote><p>async.filter([1,2,3,4,5], function(item, callback) {     <br />    log(’1.1 enter: ‘ + item);      <br />    setTimeout(function() {      <br />        log(’1.1 test: ‘ + item);      <br />        callback(item&gt;=3);      <br />    }, 200);      <br />}, function(results) {      <br />    log(’1.1 results: ‘, results);      <br />});</p>
<p> </p>
</blockquote>
<p>打印结果如下:</p>
<blockquote><p>16.739&gt; 1.1 enter: 1     <br />16.749&gt; 1.1 enter: 2      <br />16.749&gt; 1.1 enter: 3      <br />16.749&gt; 1.1 enter: 4      <br />16.749&gt; 1.1 enter: 5      <br />16.749&gt; 1.3 enter: 1      <br />16.949&gt; 1.1 test: 1      <br />16.949&gt; 1.1 test: 2      <br />16.949&gt; 1.1 test: 3      <br />16.949&gt; 1.1 test: 4      <br />16.949&gt; 1.1 test: 5      <br />16.949&gt; 1.1 results: [ 3, 4, 5 ]</p>
</blockquote>
<p>可见找到了满足条件的所有元素。</p>
<p>如果需要顺序执行,可以使用filterSeries函数,它的用法与filter一样。</p>
<p>更多详细示例:<a href=https://github.com/freewind/async_demo/blob/master/filter_reject.js>https://github.com/freewind/async_demo/blob/master/filter_reject.js</a></p>
<h2>4. reject(arr, iterator(item, callback(test)), callback(results))</h2>
<p>reject与filter相似,只是行为正好相反。当条件为true时,它将丢弃相应的元素。它也提供了并行与顺序执行两种方式。</p>
<p>并行示例,去掉所有&gt;=3的元素:</p>
<blockquote><p>async.reject([1,2,3,4,5], function(item, callback) {     <br />    log(’1.4 enter: ‘ + item);      <br />    setTimeout(function() {      <br />        log(’1.4 test: ‘ + item);      <br />        callback(item&gt;=3);      <br />    }, 200);      <br />}, function(results) {      <br />    log(’1.4 results: ‘, results);      <br />});</p>
</blockquote>
<p>打印结果如下:</p>
<blockquote><p>31.359&gt; 1.4 enter: 1     <br />31.359&gt; 1.4 enter: 2      <br />31.359&gt; 1.4 enter: 3      <br />31.359&gt; 1.4 enter: 4      <br />31.359&gt; 1.4 enter: 5      <br />31.559&gt; 1.4 test: 1      <br />31.559&gt; 1.4 test: 2      <br />31.559&gt; 1.4 test: 3      <br />31.559&gt; 1.4 test: 4      <br />31.559&gt; 1.4 test: 5      <br />31.569&gt; 1.4 results: [ 1, 2 ]</p>
</blockquote>
<p>如果想顺序执行,可使用rejectSeries,它与reject用法一样。</p>
<p>更多详细示例:<a href=https://github.com/freewind/async_demo/blob/master/filter_reject.js>https://github.com/freewind/async_demo/blob/master/filter_reject.js</a></p>
<h2>5. reduce(arr, memo, iterator(memo,item,callback), callback(err,result))</h2>
<p>Reduce可以让我们给定一个初始值,用它与集合中的每一个元素做运算,最后得到一个值。reduce从左向右来遍历元素,如果想从右向左,可使用reduceRight。</p>
<p>这里给个例子,计算出100与某个集合中所有数之和:</p>
<blockquote><p>var arr = [1,3,5];</p>
<p>async.reduce(arr, 100, function(memo, item, callback) {     <br />    log(’1.1 enter: ‘ + memo +’, ‘ + item);      <br />    setTimeout(function() {      <br />        callback(null, memo+item);      <br />    }, 100);      <br />},function(err, result) {      <br />    log(’1.1 err: ‘, err);      <br />    log(’1.1 result: ‘, result);      <br />});</p>
<p> </p>
</blockquote>
<p>将打印出结果:</p>
<blockquote><p>28.789&gt; 1.1 enter: 100, 1     <br />28.889&gt; 1.1 enter: 101, 3      <br />28.999&gt; 1.1 enter: 104, 5      <br />29.109&gt; 1.1 err:      <br />29.109&gt; 1.1 result: 109</p>
</blockquote>
<p>需要注意的是,async中的reduce,不是并行操作,而是对元素一个个顺序操作,所以当元素比较多时,性能会比较弱。如果想提高性能,可使用async.map函数,先并行得到集合中每个元素被处理之后的值,然后再使用Array.prototype.reduce函数处理,性能会快很多。</p>
<p>对于这个例子:</p>
<blockquote><p>async.reduce(arr, 100, function(memo,item,callback) {     <br />    log(’1.4 enter: ‘+memo+’,'+item);      <br />    t.inc(item, function(err,n) {      <br />        log(’1.4 handle: ‘,n);      <br />        callback(null, memo+n);      <br />    });      <br />}, function(err,result) {      <br />    log(’1.4 err: ‘, err);      <br />    log(’1.4 result: ‘, result);      <br />});</p>
<p> </p>
</blockquote>
<p>它总耗时为0.62秒。如果换成map+array.reduce:</p>
<blockquote><p>async.map(arr, function(item, callback) {     <br />    log(’1.5 enter: ‘, item);      <br />    t.inc(item, function(err,n){      <br />        log(’1.5 handle: ‘, n);      <br />        callback(null,n);      <br />    });  <br />},function(err, results) {      <br />    log(’1.5 err: ‘, err);      <br />    log(’1.5 results: ‘, results);      <br />    var sum = results.reduce(function(memo, item) {      <br />        return memo + item;      <br />    }, 100);      <br />    log(’1.5 sum: ‘, sum);      <br />});</p>
<p> </p>
</blockquote>
<p>耗时为0.21秒。</p>
<p>更多详细示例:<a href=https://github.com/freewind/async_demo/blob/master/reduce.js>https://github.com/freewind/async_demo/blob/master/reduce.js</a></p>
<h2>6. detect(array, iterator(item,callback(test)), callback(result)</h2>
<p>用于取得集合中满足条件的第一个元素。它分为并行与顺序执行两种方式,分别对应函数detect和detectSeries。</p>
<p>并行示例,找到一个奇数:</p>
<blockquote><p>var arr = [{value:1,delay:500},     <br />           {value:2,delay:200},      <br />           {value:3,delay:300}];      <br />async.detect(arr, function(item,callback){      <br />    log(’1.1 enter: ‘, item.value);      <br />    setTimeout(function() {</p>
<p>        log(’1.1 handle: ‘, item.value);     <br />        callback(n%2===1);      <br />    }, item.delay);      <br />}, function(result) {      <br />    log(’1.1 result: ‘, result);      <br />});</p>
<p> </p>
</blockquote>
<p>结果如下:</p>
<blockquote><p>09.928&gt; 1.1 enter: 1     <br />09.928&gt; 1.1 enter: 2      <br />09.928&gt; 1.1 enter: 3      <br />10.138&gt; 1.1 handle: 2      <br />10.228&gt; 1.1 handle: 3      <br />10.228&gt; 1.1 result: { value: 3, delay: 300 }      <br />10.438&gt; 1.1 handle: 1      <br />10.438&gt; 1.1 handle: 1</p>
</blockquote>
<p>可见得到了最先执行完的那个奇数3.</p>
<p>更多详细示例:<a href=https://github.com/freewind/async_demo/blob/master/detect.js>https://github.com/freewind/async_demo/blob/master/detect.js</a></p>
<p>
<h2>7. sortBy(array, iterator(item,callback(err,result)), callback(err,results))</h2>
</p>
<p>对集合内的元素进行排序,依据每个元素进行某异步操作后产生的值,从小到大排序。</p>
<p>示例:</p>
<blockquote><p>var arr = [3,6,1];</p>
<p>async.sortBy(arr, function(item, callback) {     <br />    setTimeout(function() {      <br />        callback(null,item);      <br />    }, 200);      <br />}, function(err,results) {      <br />    log(’1.1 err: ‘, err);      <br />    log(’1.1 results: ‘, results);      <br />});</p>
<p> </p>
</blockquote>
<p>打印结果如下:</p>
<blockquote><p>26.562&gt; 1.1 err: null     <br />26.562&gt; 1.1 results: [ 1, 3, 6 ]</p>
</blockquote>
<p>可以看到集合中的数据从小到大排好了序。</p>
<p>更多详细示例:<a href=https://github.com/freewind/async_demo/blob/master/sortBy.js>https://github.com/freewind/async_demo/blob/master/sortBy.js</a></p>
<p>
<h2>8. some/any(arr, iterator(item,callback(test)), callback(result))</h2>
</p>
<p>当集合中是否有至少一个元素满足条件时,最终callback得到的值为true,否则为false。它有一个别名叫any。</p>
<p>判断集合中是否有元素小于等于3:</p>
<blockquote><p>async.some([1,2,3,6], function(item,callback){     <br />    log(’1.1 enter: ‘,item);      <br />    setTimeout(function(){      <br />        log(’1.1 handle: ‘,item);      <br />        callback(item&lt;=3);      <br />    },100);    <br />}, function(result) {      <br />    log(’1.1 result: ‘, result);      <br />});</p>
</blockquote>
<p>打印结果如下:</p>
<blockquote><p>36.165&gt; 1.1 enter: 1     <br />36.165&gt; 1.1 enter: 2      <br />36.165&gt; 1.1 enter: 3      <br />36.165&gt; 1.1 enter: 6      <br />36.275&gt; 1.1 handle: 1      <br />36.275&gt; 1.1 result: true      <br />36.275&gt; 1.1 handle: 2      <br />36.275&gt; 1.1 handle: 3      <br />36.275&gt; 1.1 handle: 6</p>
<p> </p>
</blockquote>
<p>可见的确得到了结果true。</p>
<p>更多详细示例:<a href=https://github.com/freewind/async_demo/blob/master/some.js>https://github.com/freewind/async_demo/blob/master/some.js</a></p>
<h2>9. every/all(arr, iterator(item,callback), callback(result))</h2>
<p>如果集合里每一个元素都满足条件,则传给最终回调的result为true,否则为false</p>
<p>在下面的示例中,因为集合中每个元素都&lt;=10,所以最终结果为true</p>
<blockquote><p>async.every(arr, function(item,callback){     <br />    log(’1.1 enter: ‘,item);      <br />    setTimeout(function(){      <br />        log(’1.1 handle: ‘,item);      <br />        callback(item&lt;=10);      <br />    },100);    <br />}, function(result) {      <br />    log(’1.1 result: ‘, result);      <br />});</p>
<p> </p>
</blockquote>
<p>打印如下:</p>
<blockquote><p>32.113&gt; 1.1 enter: 1     <br />32.123&gt; 1.1 enter: 2      <br />32.123&gt; 1.1 enter: 3      <br />32.123&gt; 1.1 enter: 6      <br />32.233&gt; 1.1 handle: 1      <br />32.233&gt; 1.1 handle: 2      <br />32.233&gt; 1.1 handle: 3      <br />32.233&gt; 1.1 handle: 6      <br />32.233&gt; 1.1 result: true</p>
</blockquote>
<p>可见最终结果为true</p>
<p>更多详细示例:<a href=https://github.com/freewind/async_demo/blob/master/every.js>https://github.com/freewind/async_demo/blob/master/every.js</a></p>
<h2>10. concat(arr, iterator(item,callback(err,result)), callback(err,result))</h2>
<p>将合并多个异步操作的结果合并为一个数组。</p>
<p>在下面的示例中,将集合中的每一个元素都加倍:</p>
<blockquote><p><font style=background-color:

下载APP
扫码下载栗子社区APP