Skip to content

Commit a65fd11

Browse files
Yehor Khilchenkoo-rumiantsev
Yehor Khilchenko
authored andcommitted
Add support for iterable objects
1 parent d619834 commit a65fd11

15 files changed

+1064
-672
lines changed

lib/array.js

Lines changed: 93 additions & 311 deletions
Large diffs are not rendered by default.

lib/async-iterator.js

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,10 @@ class AsyncIterator {
101101
return result;
102102
}
103103

104+
async reduceRight(reducer, initialValue) {
105+
return this.reverse().reduce(reducer, initialValue);
106+
}
107+
104108
async some(predicate, thisArg) {
105109
for await (const value of this) {
106110
if (await predicate.call(thisArg, value)) {
@@ -195,6 +199,10 @@ class AsyncIterator {
195199
enumerate() {
196200
return new EnumerateIterator(this);
197201
}
202+
203+
reverse() {
204+
return new ReverseIterator(this.base);
205+
}
198206
}
199207

200208
class MapIterator extends AsyncIterator {
@@ -213,6 +221,16 @@ class MapIterator extends AsyncIterator {
213221
}
214222
}
215223

224+
class ReverseIterator extends AsyncIterator {
225+
constructor(base) {
226+
const newBase = [];
227+
for (const value of base) {
228+
newBase.unshift(value);
229+
}
230+
super(newBase);
231+
}
232+
}
233+
216234
class FilterIterator extends AsyncIterator {
217235
constructor(base, predicate, thisArg) {
218236
super(base);
@@ -403,29 +421,29 @@ class ThrottleIterator extends AsyncIterator {
403421
this.ratio = percent / (1 - percent);
404422

405423
this.sum = 0;
406-
this.count = 0;
424+
this.iterCount = 0;
407425
this.begin = Date.now();
408426
this.iterMax = this.min;
409427
}
410428

411429
async next() {
412-
if (this.iterMax > this.count) {
413-
this.count++;
414-
return this.base.next();
430+
if (this.iterMax > this.iterCount) {
431+
this.iterCount++;
432+
return await this.base.next();
415433
}
416434

417435
this.sum += Date.now() - this.begin;
418-
const itemTime = this.sum / this.count;
436+
const itemTime = this.sum / this.iterCount;
419437

420438
this.begin = Date.now();
421439
await timeout();
422440
const loopTime = Date.now() - this.begin;
423441

424442
const number = Math.max((this.ratio * loopTime) / itemTime, this.min);
425443

426-
this.iterMax = Math.round(number) + this.count;
444+
this.iterMax = Math.round(number) + this.iterCount;
427445

428-
this.count++;
446+
this.iterCount++;
429447
this.begin = Date.now();
430448
return this.base.next();
431449
}

lib/control.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
'use strict';
22

33
const common = require('@metarhia/common');
4-
5-
const { each } = require('./array');
4+
const { each } = require('./array.js');
65

76
// Executes all asynchronous functions and pass first result to callback
87
// fns - <Function[]>, callback-last / err-first

test/array.asyncMap.js

Lines changed: 83 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,62 @@
33
const metasync = require('..');
44
const metatests = require('metatests');
55

6-
metatests.test('succesfull map', test => {
7-
test.plan(2);
8-
6+
metatests.test('successful map / Array', test => {
97
const arr = [1, 2, 3];
10-
const expectedArr = [2, 4, 6];
8+
const expected = [2, 4, 6];
119

1210
metasync.asyncMap(
1311
arr,
14-
item => item * 2,
12+
(x, callback) => process.nextTick(() => callback(null, x * 2)),
1513
(err, newArr) => {
1614
test.error(err);
17-
test.strictSame(newArr, expectedArr);
15+
test.strictSame(newArr, expected);
16+
test.end();
17+
}
18+
);
19+
});
20+
21+
metatests.test('successful map / Set', test => {
22+
const set = new Set([1, 2, 3]);
23+
const expected = new Set([2, 4, 6]);
24+
25+
metasync.asyncMap(
26+
set,
27+
(x, callback) => process.nextTick(() => callback(null, x * 2)),
28+
(err, newSet) => {
29+
test.error(err);
30+
test.strictSame([...newSet], [...expected]);
31+
test.end();
32+
}
33+
);
34+
});
35+
36+
metatests.test('successful map / Map', test => {
37+
const map = new Map([[1, 'a'], [2, 'b'], [3, 'c']]);
38+
const expected = new Map([['a', 1], ['b', 2], ['c', 3]]);
39+
40+
metasync.asyncMap(
41+
map,
42+
(x, callback) => process.nextTick(() => callback(null, x.reverse())),
43+
(err, res) => {
44+
test.error(err);
45+
test.strictSame([...res], [...expected]);
46+
test.end();
47+
}
48+
);
49+
});
50+
51+
metatests.test('successful map / String', test => {
52+
const str = 'abcdefgh';
53+
const expected = 'A,B,C,D,E,F,G,H';
54+
55+
metasync.asyncMap(
56+
str,
57+
(x, callback) => process.nextTick(() => callback(null, x.toUpperCase())),
58+
(err, res) => {
59+
test.error(err);
60+
test.strictSame([...res], [...expected]);
61+
test.end();
1862
}
1963
);
2064
});
@@ -24,25 +68,26 @@ const doSmth = time => {
2468
while (Date.now() - begin < time);
2569
};
2670

27-
metatests.test('Non-blocking', test => {
71+
metatests.test('asyncMap non-blocking', test => {
2872
const ITEM_TIME = 1;
2973
const TIMER_TIME = 9;
3074
const ARRAY_SIZE = 1000;
3175
const EXPECTED_PERCENT = 0.5;
3276
const EXPECTED_DEVIATION = 0.2;
3377

3478
const arr = new Array(ARRAY_SIZE).fill(1);
35-
3679
const timer = setInterval(() => doSmth(TIMER_TIME), 1);
37-
3880
const begin = Date.now();
81+
3982
metasync.asyncMap(
4083
arr,
41-
() => doSmth(ITEM_TIME),
84+
(x, callback) => {
85+
doSmth(ITEM_TIME);
86+
callback();
87+
},
4288
{ percent: EXPECTED_PERCENT },
4389
() => {
4490
clearInterval(timer);
45-
4691
const mapTime = ITEM_TIME * ARRAY_SIZE;
4792
const allTime = Date.now() - begin;
4893
const actualPercent = mapTime / allTime;
@@ -52,3 +97,30 @@ metatests.test('Non-blocking', test => {
5297
}
5398
);
5499
});
100+
101+
metatests.test('asyncMap with error', test => {
102+
const arr = [1, 2, 3];
103+
const asyncMapError = new Error('asyncMap error');
104+
105+
metasync.asyncMap(
106+
arr,
107+
(x, callback) =>
108+
process.nextTick(() => callback(x === 2 ? asyncMapError : null, x * x)),
109+
(err, res) => {
110+
test.isError(err, asyncMapError);
111+
test.assertNot(res);
112+
test.end();
113+
}
114+
);
115+
});
116+
117+
metatests.test('asyncMap with not iterable', test => {
118+
const obj = { a: '1', b: '2', c: '3' };
119+
const expectedError = new TypeError('"items" argument is not iterable');
120+
121+
metasync.asyncMap(obj, test.mustNotCall(), (err, res) => {
122+
test.isError(err, expectedError);
123+
test.assertNot(res);
124+
test.end();
125+
});
126+
});

test/array.each.js

Lines changed: 89 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -3,71 +3,128 @@
33
const metasync = require('..');
44
const metatests = require('metatests');
55

6-
metatests.test('successful each', test => {
6+
metatests.test('successful each / Array', test => {
77
const arr = [1, 2, 3, 4];
8-
9-
const elementsSet = new Set();
10-
const expectedElementsSet = new Set(arr);
8+
const arrCopy = [];
119

1210
metasync.each(
1311
arr,
14-
(el, callback) =>
12+
(item, callback) =>
1513
process.nextTick(() => {
16-
elementsSet.add(el);
14+
arrCopy.push(item);
1715
callback(null);
1816
}),
1917
err => {
2018
test.error(err);
21-
test.strictSame(elementsSet, expectedElementsSet);
19+
test.strictSame(arrCopy, arr);
2220
test.end();
2321
}
2422
);
2523
});
2624

27-
metatests.test('each with empty array', test => {
28-
const arr = [];
25+
metatests.test('successful each / Set', test => {
26+
const set = new Set([1, 2, 3, 4, 5]);
27+
const setCopy = new Set();
2928

30-
const elementsSet = new Set();
31-
const expectedElementsSet = new Set(arr);
29+
metasync.each(
30+
set,
31+
(item, callback) =>
32+
process.nextTick(() => {
33+
setCopy.add(item);
34+
callback(null);
35+
}),
36+
err => {
37+
test.error(err);
38+
test.strictSame([...setCopy], [...set]);
39+
test.end();
40+
}
41+
);
42+
});
43+
44+
metatests.test('successful each / Map', test => {
45+
const map = new Map([[1, 'a'], [2, 'b'], [3, 'c']]);
46+
const mapCopy = new Map();
3247

3348
metasync.each(
34-
arr,
35-
(el, callback) =>
49+
map,
50+
(entry, callback) =>
51+
process.nextTick(() => {
52+
mapCopy.set(...entry);
53+
callback(null);
54+
}),
55+
err => {
56+
test.error(err);
57+
test.strictSame([...mapCopy], [...map]);
58+
test.end();
59+
}
60+
);
61+
});
62+
63+
metatests.test('successful each / String', test => {
64+
const str = 'aaabcdeefff';
65+
let strCopy = '';
66+
67+
metasync.each(
68+
str,
69+
(item, callback) =>
3670
process.nextTick(() => {
37-
elementsSet.add(el);
71+
strCopy += item;
3872
callback(null);
3973
}),
4074
err => {
4175
test.error(err);
42-
test.strictSame(elementsSet, expectedElementsSet);
76+
test.strictSame(strCopy, str);
4377
test.end();
4478
}
4579
);
4680
});
4781

82+
metatests.test('each with empty / Array', test => {
83+
const arr = [];
84+
85+
metasync.each(arr, test.mustNotCall(), err => {
86+
test.error(err);
87+
test.end();
88+
});
89+
});
90+
91+
metatests.test('each with empty / Set', test => {
92+
const set = new Set();
93+
94+
metasync.each(set, test.mustNotCall(), err => {
95+
test.error(err);
96+
test.end();
97+
});
98+
});
99+
100+
metatests.test('each with empty / Map', test => {
101+
const map = new Map();
102+
103+
metasync.each(map, test.mustNotCall(), err => {
104+
test.error(err);
105+
test.end();
106+
});
107+
});
108+
109+
metatests.test('each with empty / String', test => {
110+
const str = '';
111+
112+
metasync.each(str, test.mustNotCall(), err => {
113+
test.error(err);
114+
test.end();
115+
});
116+
});
117+
48118
metatests.test('each with error', test => {
49119
const arr = [1, 2, 3, 4];
50-
let count = 0;
51-
52-
const elementsSet = new Set();
53-
const expectedElementsCount = 2;
54-
const eachError = new Error('Each error');
120+
const eachError = new Error('each error');
55121

56122
metasync.each(
57123
arr,
58-
(el, callback) =>
59-
process.nextTick(() => {
60-
elementsSet.add(el);
61-
count++;
62-
if (count === expectedElementsCount) {
63-
callback(eachError);
64-
} else {
65-
callback(null);
66-
}
67-
}),
124+
(x, callback) =>
125+
process.nextTick(() => callback(x === 3 ? eachError : null)),
68126
err => {
69-
test.strictSame(err, eachError);
70-
test.strictSame(elementsSet.size, expectedElementsCount);
127+
test.isError(err, eachError);
71128
test.end();
72129
}
73130
);

0 commit comments

Comments
 (0)