async 之 集合

@berwin 2015-11-17 11:11:47发表于 berwin/Blog Blognode.js

之前的老文章,换了个地方写博客,,所以得重新发布下~~

Collections

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

each(arr, iterator, callback)

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

参数:

  1. arr 想要循环的数组

  2. iterator(item, callback) 一个回调函数,循环到的每一项都会调用这个函数。

    item 数组中的每一项。

    callback(err) 当完成的时候调用,应该不带参数执行,或者明确指定一个 null

  3. callback(err) 一个回调函数,用于循环完成后 或 发生错误时调用

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

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)

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

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 限制并行的最大数量

看一下例子:

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 我们在试试:

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) 一个回调函数,当所有数组执行完成,或发生错误的时候,被调用。

例如:

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 一样,不同的是,上面是 并行执行,而这个是 依次执行,与 eacheachSeries 的关系是一样的。

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 来限制并行的最大数量。

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 一个回调函数,用于执行完成后,或发生错误时调用。

例如:

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 类似,它是 依次执行

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时,抛弃之~~~

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。

加法运算:

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

乘法运算:

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,或 循环完成后执行。

例如:

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 是依次执行的。

例如:

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:

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:

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) 回调函数 resulttruefalse 取决于iterator 的运行结果。

例1:

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:

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

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

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

结果是 true,因为 109 大。

every(arr, iterator, callback)

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

例1:

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

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

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

例2:

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)

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

语法:

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

code:

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:

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 ]