Skip to content

Commit

Permalink
Require Node.js 8
Browse files Browse the repository at this point in the history
  • Loading branch information
sindresorhus committed Jul 13, 2019
1 parent 9f0b32f commit a200b62
Show file tree
Hide file tree
Showing 7 changed files with 109 additions and 121 deletions.
1 change: 0 additions & 1 deletion .travis.yml
Expand Up @@ -3,4 +3,3 @@ node_js:
- '12'
- '10'
- '8'
- '6'
79 changes: 33 additions & 46 deletions index.d.ts
Expand Up @@ -5,74 +5,61 @@ declare namespace pMap {
@default Infinity
*/
concurrency?: number;
readonly concurrency?: number;

/**
When set to `false`, instead of stopping when a promise rejects, it will wait for all the promises to settle and then reject with an [aggregated error](https://github.com/sindresorhus/aggregate-error) containing all the errors from the rejected promises.
@default true
*/
stopOnError?: boolean;
readonly stopOnError?: boolean;
}

/**
Function which is called for every item in `input`. Expected to return a `Promise` or value.
@param input - Iterated element.
@param element - Iterated element.
@param index - Index of the element in the source array.
*/
type Mapper<Element = any, NewElement = any> = (
input: Element,
element: Element,
index: number
) => NewElement | Promise<NewElement>;
}

declare const pMap: {
/**
Returns a `Promise` that is fulfilled when all promises in `input` and ones returned from `mapper` are fulfilled, or rejects if any of the promises reject. The fulfilled value is an `Array` of the fulfilled values returned from `mapper` in `input` order.
@param input - Iterated over concurrently in the `mapper` function.
@param mapper - Function which is called for every item in `input`. Expected to return a `Promise` or value.
/**
@param input - Iterated over concurrently in the `mapper` function.
@param mapper - Function which is called for every item in `input`. Expected to return a `Promise` or value.
@returns A `Promise` that is fulfilled when all promises in `input` and ones returned from `mapper` are fulfilled, or rejects if any of the promises reject. The fulfilled value is an `Array` of the fulfilled values returned from `mapper` in `input` order.
@example
```
import pMap = require('p-map');
import got = require('got');
@example
```
import pMap = require('p-map');
import got = require('got');
const sites = [
getWebsiteFromUsername('sindresorhus'), //=> Promise
'ava.li',
'todomvc.com',
'github.com'
];
const sites = [
getWebsiteFromUsername('https://sindresorhus'), //=> Promise
'https://ava.li',
'https://github.com'
];
(async () => {
const mapper = async site => {
const {requestUrl} = await got.head(site);
return requestUrl;
};
(async () => {
const mapper = async site => {
const {requestUrl} = await got.head(site);
return requestUrl;
};
const result = await pMap(sites, mapper, {concurrency: 2});
console.log(result);
//=> ['http://sindresorhus.com/', 'http://ava.li/', 'http://todomvc.com/', 'http://github.com/']
})();
```
*/
<Element, NewElement>(
input: Iterable<Element>,
mapper: pMap.Mapper<Element, NewElement>,
options?: pMap.Options
): Promise<NewElement[]>;
const result = await pMap(sites, mapper, {concurrency: 2});
// TODO: Remove this for the next major release, refactor the whole definition to:
// declare function pMap<Element, NewElement>(
// input: Iterable<Element>,
// mapper: pMap.Mapper<Element, NewElement>,
// options?: pMap.Options
// ): Promise<NewElement[]>;
// export = pMap;
default: typeof pMap;
};
console.log(result);
//=> ['https://sindresorhus.com/', 'https://ava.li/', 'https://github.com/']
})();
```
*/
declare function pMap<Element, NewElement>(
input: Iterable<Element>,
mapper: pMap.Mapper<Element, NewElement>,
options?: pMap.Options
): Promise<NewElement[]>;

export = pMap;
112 changes: 54 additions & 58 deletions index.js
@@ -1,64 +1,63 @@
'use strict';
const AggregateError = require('aggregate-error');

const pMap = (iterable, mapper, options) => new Promise((resolve, reject) => {
options = Object.assign({
concurrency: Infinity,
stopOnError: true
}, options);

if (typeof mapper !== 'function') {
throw new TypeError('Mapper function is required');
}

const {concurrency, stopOnError} = options;
module.exports = async (
iterable,
mapper,
{
concurrency = Infinity,
stopOnError = true
} = {}
) => {
return new Promise((resolve, reject) => {
if (typeof mapper !== 'function') {
throw new TypeError('Mapper function is required');
}

if (!(typeof concurrency === 'number' && concurrency >= 1)) {
throw new TypeError(`Expected \`concurrency\` to be a number from 1 and up, got \`${concurrency}\` (${typeof concurrency})`);
}
if (!(typeof concurrency === 'number' && concurrency >= 1)) {
throw new TypeError(`Expected \`concurrency\` to be a number from 1 and up, got \`${concurrency}\` (${typeof concurrency})`);
}

const ret = [];
const errors = [];
const iterator = iterable[Symbol.iterator]();
let isRejected = false;
let isIterableDone = false;
let resolvingCount = 0;
let currentIndex = 0;
const ret = [];
const errors = [];
const iterator = iterable[Symbol.iterator]();
let isRejected = false;
let isIterableDone = false;
let resolvingCount = 0;
let currentIndex = 0;

const next = () => {
if (isRejected) {
return;
}
const next = () => {
if (isRejected) {
return;
}

const nextItem = iterator.next();
const i = currentIndex;
currentIndex++;
const nextItem = iterator.next();
const i = currentIndex;
currentIndex++;

if (nextItem.done) {
isIterableDone = true;
if (nextItem.done) {
isIterableDone = true;

if (resolvingCount === 0) {
if (!stopOnError && errors.length !== 0) {
reject(new AggregateError(errors));
} else {
resolve(ret);
if (resolvingCount === 0) {
if (!stopOnError && errors.length !== 0) {
reject(new AggregateError(errors));
} else {
resolve(ret);
}
}
}

return;
}
return;
}

resolvingCount++;
resolvingCount++;

Promise.resolve(nextItem.value)
.then(element => mapper(element, i))
.then(
value => {
ret[i] = value;
(async () => {
try {
const element = await nextItem.value;
ret[i] = await mapper(element, i);
resolvingCount--;
next();
},
error => {
} catch (error) {
if (stopOnError) {
isRejected = true;
reject(error);
Expand All @@ -68,18 +67,15 @@ const pMap = (iterable, mapper, options) => new Promise((resolve, reject) => {
next();
}
}
);
};
})();
};

for (let i = 0; i < concurrency; i++) {
next();
for (let i = 0; i < concurrency; i++) {
next();

if (isIterableDone) {
break;
if (isIterableDone) {
break;
}
}
}
});

module.exports = pMap;
// TODO: Remove this for the next major release
module.exports.default = pMap;
});
};
14 changes: 11 additions & 3 deletions index.test-d.ts
Expand Up @@ -2,9 +2,17 @@ import {expectType} from 'tsd';
import pMap = require('.');
import {Options, Mapper} from '.';

const sites = ['ava.li', 'todomvc.com', 'github.com'];

const numbers = [0, 1, 2];
const sites = [
'https://sindresorhus.com',
'https://ava.li',
'https://github.com'
];

const numbers = [
0,
1,
2
];

const asyncMapper = async (site: string) => site;
const asyncSyncMapper = (site: string, index: number) =>
Expand Down
10 changes: 5 additions & 5 deletions package.json
Expand Up @@ -10,7 +10,7 @@
"url": "sindresorhus.com"
},
"engines": {
"node": ">=6"
"node": ">=8"
},
"scripts": {
"test": "xo && ava && tsd"
Expand Down Expand Up @@ -38,13 +38,13 @@
"bluebird"
],
"dependencies": {
"aggregate-error": "^2.0.0"
"aggregate-error": "^3.0.0"
},
"devDependencies": {
"ava": "^1.4.1",
"ava": "^2.2.0",
"delay": "^4.1.0",
"in-range": "^1.0.0",
"random-int": "^1.0.0",
"in-range": "^2.0.0",
"random-int": "^2.0.0",
"time-span": "^3.1.0",
"tsd": "^0.7.2",
"xo": "^0.24.0"
Expand Down
9 changes: 4 additions & 5 deletions readme.md
Expand Up @@ -19,10 +19,9 @@ const pMap = require('p-map');
const got = require('got');

const sites = [
getWebsiteFromUsername('sindresorhus'), //=> Promise
'ava.li',
'todomvc.com',
'github.com'
getWebsiteFromUsername('https://sindresorhus'), //=> Promise
'https://ava.li',
'https://github.com'
];

(async () => {
Expand All @@ -34,7 +33,7 @@ const sites = [
const result = await pMap(sites, mapper, {concurrency: 2});

console.log(result);
//=> ['http://sindresorhus.com/', 'http://ava.li/', 'http://todomvc.com/', 'http://github.com/']
//=> ['https://sindresorhus.com/', 'https://ava.li/', 'https://github.com/']
})();
```

Expand Down
5 changes: 2 additions & 3 deletions test.js
Expand Up @@ -31,13 +31,13 @@ const mapper = ([value, ms]) => delay(ms, {value});
test('main', async t => {
const end = timeSpan();
t.deepEqual(await pMap(input, mapper), [10, 20, 30]);
t.true(inRange(end(), 290, 430));
t.true(inRange(end(), {start: 290, end: 430}));
});

test('concurrency: 1', async t => {
const end = timeSpan();
t.deepEqual(await pMap(input, mapper, {concurrency: 1}), [10, 20, 30]);
t.true(inRange(end(), 590, 760));
t.true(inRange(end(), {start: 590, end: 760}));
});

test('concurrency: 4', async t => {
Expand Down Expand Up @@ -79,7 +79,6 @@ test('async with concurrency: 2 (out of order time sequence)', async t => {

test('enforce number in options.concurrency', async t => {
await t.throwsAsync(pMap([], () => {}, {concurrency: 0}), TypeError);
await t.throwsAsync(pMap([], () => {}, {concurrency: undefined}), TypeError);
await t.notThrowsAsync(pMap([], () => {}, {concurrency: 1}));
await t.notThrowsAsync(pMap([], () => {}, {concurrency: 10}));
await t.notThrowsAsync(pMap([], () => {}, {concurrency: Infinity}));
Expand Down

0 comments on commit a200b62

Please sign in to comment.