async 之 集合
之前的老文章，换了个地方写博客，，所以得重新发布下~~
# Collections

async是Nodejs中的一个非常常用的工具模块，其中方法有很多，主要分3大类（集合，流程控制，工具），前几天刚说了 **流程控制** 的一些常用方法，今天就简单说说 **集合** 的一些常用方法
### each(arr, iterator, callback)

很简单，看方法名就知道这是一个循环。

参数：
1. `arr` 想要循环的数组
2. `iterator(item, callback)` 一个回调函数，循环到的每一项都会调用这个函数。
   
   `item` 数组中的每一项。
   
   `callback(err)` 当完成的时候调用，应该不带参数执行，或者明确指定一个 `null`
3. `callback(err)` 一个回调函数，用于循环完成后 或 发生错误时调用

这个循环与系统提供的`for`循环等是一样的。属于并行执行的循环，请看下面的例子：

``` javascript
var arr = [ {name: 'a', delay: 200}, {name: 'b', delay: 100}, {name: 'c', delay: 300} ];

function iterator (item, done) {
    console.log('start:' + item.name);
    setTimeout(function(){
        console.log('end: ' + item.name);
        done(null);
    }, item.delay);
}

async.each(arr, iterator, function (err) {
    console.log('err: ' + err);
});
```

输出结果是：

```
start:a
start:b
start:c
end: b
end: a
end: c
err: undefined
```

先输出3个 **start**， 然后输出3个 **end** 注意顺序， 最后是 **err** 

由此可以看出，它与正常的循环并无两样。
### eachSeries(arr, iterator, callback)

语法与上面的用法一样。不同的是，上面我称为 **并行执行的循环**，而这个我称为，**依次执行的循环**，请看下面的例子：

``` javascript
var arr = [ {name: 'a', delay: 200}, {name: 'b', delay: 100}, {name: 'c', delay: 300} ];

function iterator (item, done) {
    console.log('start:' + item.name);
    setTimeout(function(){
        console.log('end: ' + item.name);
        done(null);
    }, item.delay);
}

async.eachSeries(arr, iterator, function (err) {
    console.log('err: ' + err);
});
```

细心的同学可能已经发现问题了。这段代码与上面的代码几乎一模一样。唯一不一样的地方只有`async.each` 改成了 `async.eachSeries`

结果为：

```
start:a
end: a
start:b
end: b
start:c
end: c
err: undefined
```

看完结果同学们就可以理解，为什么称 `eachSeries` 为依次执行的循环了。
### eachLimit(arr, limit, iterator, callback)

如果非要起一个名字的话。我称它为 **分批执行** 也可以说是 **限制并行数量的循环**

用法与上面大致一样。只不过多了一个参数 **limit**
- `limit` 限制并行的最大数量

看一下例子：

``` javascript
var arr = [ {name: 'a', delay: 200}, {name: 'b', delay: 100}, {name: 'c', delay: 300} ];

function iterator (item, done) {
    console.log('start:' + item.name);
    setTimeout(function(){
        console.log('end: ' + item.name);
        done(null);
    }, item.delay);
}

async.eachLimit(arr, 1, iterator, function (err) {
    console.log('err: ' + err);
});
```

  结果为：

```
start:a
end: a
start:b
end: b
start:c
end: c
err: undefined
```

可以看出，如果 `limit` 的为 `1`，那么就与 `eachSeries` 一样，一条一条依次执行。

如果 `limit` 的数量为 `3` 我们在试试：

``` javascript
var arr = [ {name: 'a', delay: 200}, {name: 'b', delay: 100}, {name: 'c', delay: 300} ];

function iterator (item, done) {
    console.log('start:' + item.name);
    setTimeout(function(){
        console.log('end: ' + item.name);
        done(null);
    }, item.delay);
}

async.eachLimit(arr, 3, iterator, function (err) {
    console.log('err: ' + err);
});
```

输出结果为：

```
start:a
start:b
start:c
end: b
end: a
end: c
err: undefined
```

相信同学们已经看明白，我就不多做解释了。
### map(arr, iterator, callback)

`map` 通俗点说，就是通过一个转换函数（iterator），把数组中的每个值映射到一个新的数组中。（产生一个新的数组）

参数：
1. `arr` 想要循环的数组
2. `iterator(item, callback)` 一个回调函数，循环到得每一项都会调用这个函数
   
   `callback(err, transformed)` 当程序执行完时，调用此参数(必须调用此参数)
3. `callback(err, results)` 一个回调函数，当所有数组执行完成，或发生错误的时候，被调用。

例如：

``` javascript
var arr = [ {name: 'a', delay: 200}, {name: 'b', delay: 100}, {name: 'c', delay: 300} ];

function iterator (item, done) {
    console.log( 'start:', item.name );
    setTimeout(function () {
        console.log( 'end:', item.name );
        done(null, item.name += '!');
    }, item.delay);    
}

async.map(arr, iterator, function (err, result) {
    console.log( 'err: ', err );
    console.log( 'result:', result );
});
```

结果为：

```
start: a
start: b
start: c
end: b
end: a
end: c
err:  undefined
result: [ 'a!', 'b!', 'c!' ]
```
### mapSeries(arr, iterator, callback)

语法与上面 `map` 一样，不同的是，上面是 **并行执行**，而这个是 **依次执行**，与 `each` 和 `eachSeries` 的关系是一样的。

``` javascript
var arr = [ {name: 'a', delay: 200}, {name: 'b', delay: 100}, {name: 'c', delay: 300} ];

function iterator (item, done) {
    console.log( 'start:', item.name );
    setTimeout(function () {
        console.log( 'end:', item.name );
        done(null, item.name += '!');
    }, item.delay);    
}

async.mapSeries(arr, iterator, function (err, result) {
    console.log( 'err: ', err );
    console.log( 'result:', result );
});
```

代码与 `map` 几乎一样。只是把 `async.map` 改成了 `async.mapSeries`，输出结果为：

```
start: a
end: a
start: b
end: b
start: c
end: c
err:  undefined
result: [ 'a!', 'b!', 'c!' ]
```

从输出结构可以看出，它是依次执行的。
### mapLimit(arr, limit, iterator, callback)

与 `map` 一样，但比 `map` 多了一个参数 **`limit`** 来限制并行的最大数量。

``` javascript
var arr = [ {name: 'a', delay: 200}, {name: 'b', delay: 100}, {name: 'c', delay: 300} ];

function iterator (item, done) {
    console.log( 'start:', item.name );
    setTimeout(function () {
        console.log( 'end:', item.name );
        done(null, item.name += '!');
    }, item.delay);    
}

async.mapLimit(arr, 1, iterator, function (err, result) {
    console.log( 'err: ', err );
    console.log( 'result:', result );
});
```

`limit` 设置为 `1` ，结果为：

```
start: a
end: a
start: b
end: b
start: c
end: c
err:  undefined
result: [ 'a!', 'b!', 'c!' ]
```

结果与 `mapSeries` 一样，换成 `2` 结果为：

```
start: a
start: b
end: b
start: c
end: a
end: c
err:  undefined
result: [ 'a!', 'b!', 'c!' ]
```

换成 `3` 结果为：

```
start: a
start: b
start: c
end: b
end: a
end: c
err:  undefined
result: [ 'a!', 'b!', 'c!' ]
```
### filter(arr, iterator, callback)

遍历 `arr` 中的每个值，返回包含所有通过 `iterator` 真值检测的元素值。**这个操作是并行的，但返回的结果是顺序的**。

参数：
1. `arr` 一个数组，用于遍历
2. `iterator(item, callback)` 一个函数，用于真值检测
   
   `item` 数组中的每一项
   
   `callback(truthValue)` 完成时调用，必须带一个布尔参数
3. `callback` 一个回调函数，用于执行完成后，或发生错误时调用。

例如：

``` javascript
var arr = [ {n: 1, delay: 200}, {n: 2, delay: 100}, {n: 3, delay: 300}, {n: 4, delay: 500}, {n: 5, delay: 100} ];

function iterator (item, done) {
    console.log( 'start:', item.n );
    setTimeout(function () {
        console.log( 'end:', item.n );
        done( item.n > 2 );
    }, item.delay);    
}

async.filter(arr, iterator, function (result) {
    console.log( 'result:', result );
});
```

输出结果为：

```
start: 1
start: 2
start: 3
start: 4
start: 5
end: 2
end: 5
end: 1
end: 3
end: 4
result: [ { n: 3, delay: 300 }, { n: 4, delay: 500 }, { n: 5, delay: 100 } ]
```
### filterSeries(arr, iterator, callback)

与上面 `filter` 类似，它是 **依次执行**。

``` javascript
var arr = [ {n: 1, delay: 200}, {n: 2, delay: 100}, {n: 3, delay: 300}, {n: 4, delay: 500}, {n: 5, delay: 100} ];

function iterator (item, done) {
    console.log( 'start:', item.n );
    setTimeout(function () {
        console.log( 'end:', item.n );
        done( item.n > 2 );
    }, item.delay);    
}

async.filterSeries(arr, iterator, function (result) {
    console.log( 'result:', result );
});
```

把 `async.filter` 改成 `async.filterSeries` 输出结果为：

```
start: 1
end: 1
start: 2
end: 2
start: 3
end: 3
start: 4
end: 4
start: 5
end: 5
result: [ { n: 3, delay: 300 }, { n: 4, delay: 500 }, { n: 5, delay: 100 } ]
```
### reject(arr, iterator, callback)

reject跟filter正好相反，当检测为true时，抛弃之~~~

``` javascript
var arr = [ {n: 1, delay: 200}, {n: 2, delay: 100}, {n: 3, delay: 300}, {n: 4, delay: 500}, {n: 5, delay: 100} ];

function iterator (item, done) {
    console.log( 'start:', item.n );
    setTimeout(function () {
        console.log( 'end:', item.n );
        done( item.n > 2 );
    }, item.delay);    
}

async.reject(arr, iterator, function (result) {
    console.log( 'result:', result );
});
```

输出结果为：

```
start: 1
start: 2
start: 3
start: 4
start: 5
end: 2
end: 5
end: 1
end: 3
end: 4
result: [ { n: 1, delay: 200 }, { n: 2, delay: 100 } ]
```
### rejectSeries(arr, iterator, callback)

如果说reject是并行（异步的）的，那么rejectSeries就是串行的（同步的），前面写了那么多 **并行** 和 **串行** 的比较例子，从现在往后就不举例说明了。
### reduce(arr, memo, iterator, callback)

Reduce可以让我们给定一个初始值，用它与集合中的每一个元素做运算**（前一次的运算结果与下一个值做运算）**，最后得到一个值。reduce从左向右来遍历元素，如果想从右向左，可使用reduceRight。

加法运算：

``` javascript
var arr = [1, 3, 5];

function iterator (memo, item, done) {
    console.log( memo, item );
    setTimeout(function () {
        done( null, item + memo );
    }, 300);
}

async.reduce(arr, 2, iterator, function (err, result) {
    console.log( 'result:', result );
});
```

输出结果为：

```
2 1
3 3
6 5
result: 11
```

乘法运算：

``` javascript
var arr = [1, 3, 5];

function iterator (memo, item, done) {
    console.log( memo, item );
    setTimeout(function () {
        done( null, item * memo );
    }, 300);
}

async.reduce(arr, 2, iterator, function (err, result) {
    console.log( 'result:', result );
});
```

输出结果为：

```
2 1
2 3
6 5
result: 30
```
### reduceRight(arr, memo, iterator, callback)

与 `reduce` 一样，不同的是，**`reduceRight`** 是从右向左计算。
### detect(arr, iterator, callback)

用于取得集合中满足条件的第一个元素（并行执行）。

语法：
1. `arr` 一个数组
2. `iterator(item, callback)` 回调函数，用于处理逻辑（迭代器）
   
   `item` 数组中的每一项
   
   `callback(truthValue)` 程序完成后执行。必须传入布尔值。
3. `callback(result)` 回调函数， `iterator` 第一次返回 `true`，或 循环完成后执行。

例如：

``` javascript
var arr = [
    {n:1,delay:500},
    {n:2,delay:200},
    {n:3,delay:300}
];

function iterator (item, done) {
    console.log( 'start:', item.n );
    setTimeout(function () {
        console.log( 'end:', item.n )
        done( item.n > 1 );
    }, item.delay );
}

async.detect(arr, iterator, function (result) {
    console.log( 'result:', result );
});
```

输出结果为：

```
start: 1
start: 2
start: 3
end: 2
result: { n: 2, delay: 200 }
end: 3
end: 1
```

可以看出，输出 `2` 的时候，就执行 `callback(result)` 了，也可以看出。`detect`是并行执行的。
### detectSeries(arr, iterator, callback)

与 `detect` 类似。不过 `detectSeries` 是依次执行的。

例如：

``` javascript
var arr = [
    {n:1,delay:500},
    {n:2,delay:200},
    {n:3,delay:300}
];

function iterator (item, done) {
    console.log( 'start:', item.n );
    setTimeout(function () {
        console.log( 'end:', item.n )
        done( item.n > 1 );
    }, item.delay );
}

async.detectSeries(arr, iterator, function (result) {
    console.log( 'result:', result );
});
```

输出结果为：

```
start: 1
end: 1
start: 2
end: 2
result: { n: 2, delay: 200 }
```
### sortBy(arr, iterator, callback)

对集合内的元素进行排序，根据每个元素进行某异步操作后产生的值，从小到大排序。

语法：
1. `arr` 一个数组
2. `iterator(item, callback)` 一个回调函数，循环到得每一项都会执行。
   
   `item` 数组中的每一项
   
   `callback(err, sortValue)` 完成时调用。
3. `callback(err, results)` 一个回调函数，所有 `iterator` 完成后或发生错误时执行。

例1：

``` javascript
var arr = [2, 5, 9, 10, 22, 1, 7, 20];

function iterator (item, done) {
    done( null, item );
}

async.sortBy(arr, iterator, function (err, result) {
    console.log( result ); // [ 1, 2, 5, 7, 9, 10, 20, 22 ]
});
```

例2：

``` javascript
var arr = [2, 5, 9, 10, 22, 1, 7, 20];

function iterator (item, done) {
    done( null, item * -1 );
}

async.sortBy(arr, iterator, function (err, result) {
    console.log( result ); // [ 22, 20, 10, 9, 7, 5, 2, 1 ]
});
```
### some(arr, iterator, callback)

判断集合中是否有至少一个元素满足条件，如果是最终callback得到的值为true，否则为false.

参数：
1. `arr` 一个数组
2. `iterator(item, callback)` 一个回调函数，循环到得每一项都会执行。
   
   `callback(truthValue)` 必须传递一个布尔值。
3. `callback(result)` 回调函数 `result`为 `true` 或 `false` 取决于`iterator` 的运行结果。

例1：

``` javascript
var arr = [2, 5, 9, 10];

function iterator (item, done) {
    done( item > 10 );
}

async.some(arr, iterator, function (result) {
    console.log( result ); // false
});
```

结果是 `false`，因为数组中，没有比10大的数。

例2：

``` javascript
var arr = [2, 5, 9, 10];

function iterator (item, done) {
    done( item > 9 );
}

async.some(arr, iterator, function (result) {
    console.log( result ); // true
});
```

结果是 `true`，因为 `10` 比 `9` 大。
### every(arr, iterator, callback)

如果集合里每一个元素都满足条件，则传给最终回调的result为true，否则为false

例1：

``` javascript
var arr = [2, 5, 9, 10];

function iterator (item, done) {
    done( item > 1 );
}

async.every(arr, iterator, function (result) {
    console.log( result ); // true
});
```

例2：

``` javascript
var arr = [2, 5, 9, 10];

function iterator (item, done) {
    done( item > 5 );
}

async.every(arr, iterator, function (result) {
    console.log( result ); // false
});
```
### concat(arr, iterator, callback)

将多个异步操作的结果合并为一个数组。

语法：

``` javascript
concat(arr, iterator(item,callback(err, result)), callback(err, result))
```

code：

``` javascript
var arr = [
    {
        list : [1,2,3,4],
        delay : 200
    },{
        list : [5,6,7],
        delay : 100
    },{
        list : [8,9],
        delay : 300
    }
];

function iterator (item, done) {
    console.log( 'start:', item.list )
    setTimeout(function () {
        console.log( 'end:', item.list )
        done( null, item.list );
    }, item.delay);
}

async.concat(arr, iterator, function (err, result) {
    console.log( result ); // [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
});
```

结果为：

```
start: [ 1, 2, 3, 4 ]
start: [ 5, 6, 7 ]
start: [ 8, 9 ]
end: [ 5, 6, 7 ]
end: [ 1, 2, 3, 4 ]
end: [ 8, 9 ]
[ 5, 6, 7, 1, 2, 3, 4, 8, 9 ]
```

通过结果，会发现。这是一个并行的操作。合并之后的顺序是不固定的。
### concatSeries(arr, iterator, callback)

与 `concat` 类似，不过 `concatSeries` 是串行的。

code：

``` javascript
var arr = [
    {
        list : [1,2,3,4],
        delay : 200
    },{
        list : [5,6,7],
        delay : 100
    },{
        list : [8,9],
        delay : 300
    }
];

function iterator (item, done) {
    console.log( 'start:', item.list )
    setTimeout(function () {
        console.log( 'end:', item.list )
        done( null, item.list );
    }, item.delay);
}

async.concatSeries(arr, iterator, function (err, result) {
    console.log( result ); // [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
});
```

结果是：

```
start: [ 1, 2, 3, 4 ]
end: [ 1, 2, 3, 4 ]
start: [ 5, 6, 7 ]
end: [ 5, 6, 7 ]
start: [ 8, 9 ]
end: [ 8, 9 ]
[ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
```
