From 778a7da7266f5588ad4964f4b61fc65f6911352f Mon Sep 17 00:00:00 2001 From: papb Date: Sat, 5 Jun 2021 20:42:23 -0300 Subject: [PATCH 1/9] Allow stopping iteration via `pMap.stop` --- index.d.ts | 87 +++++++++++++++++++++++++++++++++++++++++++++---- index.js | 48 ++++++++++++++++++++++----- index.test-d.ts | 28 +++++++++++++++- readme.md | 67 ++++++++++++++++++++++++++++++++++++- test.js | 70 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 283 insertions(+), 17 deletions(-) diff --git a/index.d.ts b/index.d.ts index 7dfbb7f..2c84a6a 100644 --- a/index.d.ts +++ b/index.d.ts @@ -1,3 +1,7 @@ +declare const stop: unique symbol; + +export type StopSymbol = typeof stop; + export interface Options { /** Number of concurrently pending promises returned by `mapper`. @@ -16,6 +20,46 @@ export interface Options { readonly stopOnError?: boolean; } +export interface OngoingMappingsStopOptions { + /** + Whether or not to remove all holes from the result array (caused by pending mappings). + + @default false + */ + readonly collapse?: boolean; + + /** + Value to use as immediate result for pending mappings, replacing holes from the result array. + + This option is ignored if `collapse` is set to `true`. + + @default undefined + */ + readonly fillWith?: unknown; +} + +export interface StopOptions { + /** + Value to provide as result for this iteration. + + @default undefined + */ + readonly value?: NewElement; + + /** + Options to configure what `pMap` must do with any concurrent ongoing mappings at the moment `stop` is called. + */ + readonly ongoingMappings?: OngoingMappingsStopOptions; +} + +type BaseStopValueWrapper = { + [stop]: Required>; +}; + +export type StopValueWrapper = NewElement extends any ? BaseStopValueWrapper : never; + +type MaybeWrappedInStop = NewElement | StopValueWrapper; + /** Function which is called for every item in `input`. Expected to return a `Promise` or value. @@ -25,7 +69,7 @@ Function which is called for every item in `input`. Expected to return a `Promis export type Mapper = ( element: Element, index: number -) => NewElement | Promise; +) => MaybeWrappedInStop | Promise>; /** @param input - Iterated over concurrently in the `mapper` function. @@ -54,8 +98,39 @@ console.log(result); //=> ['https://sindresorhus.com/', 'https://avajs.dev/', 'https://github.com/'] ``` */ -export default function pMap( - input: Iterable, - mapper: Mapper, - options?: Options -): Promise; +declare const pMap: { + ( + input: Iterable, + mapper: Mapper, + options?: Options + ): Promise; + + /** + Creates a special object that indicates to `pMap` that iteration must stop immediately. This object should just be returned from within the mapper (and not used directly for anything). + + @example + ``` + import pMap from 'p-map'; + import got from 'got'; + + const numbers = Array.from({ length: 2000 }).map((_, i) => i + 1); + //=> [1, 2, ..., 1999, 2000] + + const mapper = async number => { + if (number !== 404) { + const { transcript } = await got(`https://xkcd.com/${number}/info.0.json`).json(); + if (/unicorn/.test(transcript)) { + console.log('Found a XKCD comic with an unicorn:', number); + return pMap.stop(); + } + } + }; + + await pMap(numbers, mapper, { concurrency: 50 }); + //=> Found a XKCD comic with an unicorn: 948 + ``` + */ + stop: (options?: StopOptions) => StopValueWrapper; +}; + +export default pMap; diff --git a/index.js b/index.js index 15f3da2..5314058 100644 --- a/index.js +++ b/index.js @@ -1,5 +1,7 @@ import AggregateError from 'aggregate-error'; +const stopSymbol = Symbol('pMap.stop'); + export default async function pMap( iterable, mapper, @@ -20,13 +22,13 @@ export default async function pMap( const result = []; const errors = []; const iterator = iterable[Symbol.iterator](); - let isRejected = false; + const pendingIndexes = new Set(); + let finished = false; let isIterableDone = false; - let resolvingCount = 0; let currentIndex = 0; const next = () => { - if (isRejected) { + if (finished) { return; } @@ -37,7 +39,7 @@ export default async function pMap( if (nextItem.done) { isIterableDone = true; - if (resolvingCount === 0) { + if (pendingIndexes.size === 0) { if (!stopOnError && errors.length > 0) { reject(new AggregateError(errors)); } else { @@ -48,21 +50,47 @@ export default async function pMap( return; } - resolvingCount++; + pendingIndexes.add(index); (async () => { try { const element = await nextItem.value; + + if (finished) { + return; + } + result[index] = await mapper(element, index); - resolvingCount--; - next(); + + if (finished) { + return; + } + + pendingIndexes.delete(index); + + if (result[index] && result[index][stopSymbol]) { + finished = true; + const stopConfig = result[index][stopSymbol]; + result[index] = stopConfig.value; + if (stopConfig.ongoingMappings.collapse) { + resolve(result.flat(0)); + } else { + for (const pendingIndex of pendingIndexes) { + result[pendingIndex] = stopConfig.ongoingMappings.fillWith; + } + + resolve(result); + } + } else { + next(); + } } catch (error) { if (stopOnError) { - isRejected = true; + finished = true; reject(error); } else { errors.push(error); - resolvingCount--; + pendingIndexes.delete(index); next(); } } @@ -78,3 +106,5 @@ export default async function pMap( } }); } + +pMap.stop = ({value, ongoingMappings = {}} = {}) => ({[stopSymbol]: {value, ongoingMappings}}); diff --git a/index.test-d.ts b/index.test-d.ts index f5595ce..88e5959 100644 --- a/index.test-d.ts +++ b/index.test-d.ts @@ -1,4 +1,4 @@ -import {expectType, expectAssignable} from 'tsd'; +import {expectType, expectAssignable, expectNotAssignable} from 'tsd'; import pMap, {Options, Mapper} from './index.js'; const sites = [ @@ -18,13 +18,26 @@ const asyncSyncMapper = async (site: string, index: number): Promise => index > 1 ? site : Promise.resolve(site); const multiResultTypeMapper = async (site: string, index: number): Promise => index > 1 ? site.length : site; +const mapperStoppingEarly1 = async (site: string, index: number) => + index > 1 ? pMap.stop({value: site.length}) : site; +const mapperStoppingEarly2 = async (site: string, index: number) => + index > 1 ? pMap.stop({value: index > 2 ? site.length : site}) : site; +const mapperStoppingEarly3 = async (site: string, index: number) => + index > 1 ? pMap.stop({value: index > 2 ? site.length : (index > 3 ? Date.now() : site)}) : true; expectAssignable(asyncMapper); expectAssignable>(asyncMapper); expectAssignable(asyncSyncMapper); +expectAssignable>(asyncSyncMapper); expectAssignable>>(asyncSyncMapper); expectAssignable(multiResultTypeMapper); expectAssignable>(multiResultTypeMapper); +expectAssignable(mapperStoppingEarly1); +expectAssignable>(mapperStoppingEarly1); +expectAssignable(mapperStoppingEarly2); +expectAssignable>(mapperStoppingEarly2); +expectAssignable(mapperStoppingEarly3); +expectAssignable>(mapperStoppingEarly3); expectAssignable({}); expectAssignable({concurrency: 0}); @@ -40,3 +53,16 @@ expectType>(pMap(sites, (site: string) => site)); expectType>(pMap(sites, (site: string) => site.length)); expectType>(pMap(numbers, (number: number) => number * 2)); + +expectType>(pMap(numbers, (number: number) => number * 2)); + +pMap.stop(); +pMap.stop({}); +pMap.stop({value: 123}); +pMap.stop({ongoingMappings: {}}); +pMap.stop({ongoingMappings: {collapse: true}}); +pMap.stop({ongoingMappings: {fillWith: 'hello'}}); +pMap.stop({value: Date.now(), ongoingMappings: {collapse: false, fillWith: 'hello'}}); + +const shouldBeUnusableDirectly = pMap.stop({value: 123}); +expectNotAssignable(shouldBeUnusableDirectly); diff --git a/readme.md b/readme.md index 8b666a4..557d518 100644 --- a/readme.md +++ b/readme.md @@ -4,7 +4,11 @@ Useful when you need to run promise-returning & async functions multiple times with different inputs concurrently. -This is different from `Promise.all()` in that you can control the concurrency and also decide whether or not to stop iterating when there's an error. +This is different from `Promise.all()` in that you can: + +* Control the concurrency +* Decide whether or not to stop iterating when there's an error +* Stop iterating at any point (like `break` in standard loops) ## Install @@ -35,6 +39,29 @@ console.log(result); //=> ['https://sindresorhus.com/', 'https://avajs.dev/', 'https://github.com/'] ``` +### Breaking from iteration + +```js +import pMap from 'p-map'; +import got from 'got'; + +const numbers = Array.from({ length: 2000 }).map((_, i) => i + 1); +//=> [1, 2, ..., 1999, 2000] + +const mapper = async number => { + if (number !== 404) { + const { transcript } = await got(`https://xkcd.com/${number}/info.0.json`).json(); + if (/unicorn/.test(transcript)) { + console.log('Found a XKCD comic with an unicorn:', number); + return pMap.stop(); + } + } +}; + +await pMap(numbers, mapper, { concurrency: 50 }); +//=> Found a XKCD comic with an unicorn: 948 +``` + ## API ### pMap(input, mapper, options?) @@ -72,6 +99,44 @@ Default: `true` 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. +### pMap.stop(options?) + +Creates a special object that indicates to `pMap` that iteration must stop immediately. This object should just be returned from within the mapper (and not used directly for anything). + +#### options + +Type: `object` + +##### value + +Type: `any`\ +Default: `undefined` + +Value to provide as result for this iteration. + +##### ongoingMappings + +Type: `object` + +Options to configure what `pMap` must do with any concurrent ongoing mappings at the moment `stop` is called. + +###### collapse + +Type: `boolean`\ +Default: `false` + +Whether or not to remove all holes from the result array (caused by pending mappings). + +###### fillWith + +Type: `any`\ +Default: `undefined` + +Value to use as immediate result for pending mappings, replacing holes from the result array. + +This option is ignored if `collapse` is set to `true`. + + ## p-map for enterprise Available as part of the Tidelift Subscription. diff --git a/test.js b/test.js index 35d361a..4bfaa1d 100644 --- a/test.js +++ b/test.js @@ -107,3 +107,73 @@ test('aggregate errors when stopOnError is false', async t => { await t.throwsAsync(pMap(errorInput1, mapper, {concurrency: 1, stopOnError: false}), {instanceOf: AggregateError, message: /foo(.|\n)*bar/}); await t.throwsAsync(pMap(errorInput2, mapper, {concurrency: 1, stopOnError: false}), {instanceOf: AggregateError, message: /bar(.|\n)*foo/}); }); + +test('stop early, finite concurrency, default ongoingMappings behavior', async t => { + const input = [ + [1, 300], + [2, 100], + [() => pMap.stop({value: 3}), 200], + [4, 300], + [5, 50], + [6, 100], + [7, 50] + ]; + + const result = await pMap(input, mapper, {concurrency: 4}); + t.deepEqual(result, [undefined, 2, 3, undefined, 5, undefined]); +}); + +test('stop early, finite concurrency, collapse', async t => { + const input = [ + [1, 300], + [2, 100], + [() => pMap.stop({value: 3, ongoingMappings: {collapse: true}}), 200], + [4, 300], + [5, 50], + [6, 100], + [7, 50] + ]; + + const result = await pMap(input, mapper, {concurrency: 4}); + t.deepEqual(result, [2, 3, 5]); +}); + +test('stop early, finite concurrency, fill with zeros', async t => { + const input = [ + [1, 300], + [2, 100], + [() => pMap.stop({value: 3, ongoingMappings: {fillWith: 0}}), 200], + [4, 300], + [5, 50], + [6, 100], + [7, 50] + ]; + + const result = await pMap(input, mapper, {concurrency: 4}); + t.deepEqual(result, [0, 2, 3, 0, 5, 0]); +}); + +test('stop early, collapse overrides fillWith', async t => { + const input = [ + [1, 100], + [2, 10], + [() => pMap.stop({value: 3, ongoingMappings: {collapse: true, fillWith: 0}}), 20], + [4, 50], + [5, 50] + ]; + + const result = await pMap(input, mapper); + t.deepEqual(result, [2, 3]); +}); + +test('stop early, stop value is optional', async t => { + const input = [ + [1, 10], + [2, 10], + [() => pMap.stop(), 10], + [4, 10] + ]; + + const result = await pMap(input, mapper, {concurrency: 1}); + t.deepEqual(result, [1, 2, undefined]); +}); From cd0343ae9c6a32fa1f423368a1f358924c514463 Mon Sep 17 00:00:00 2001 From: Pedro Augusto de Paula Barbosa Date: Sat, 26 Jun 2021 15:20:12 -0300 Subject: [PATCH 2/9] refactor `finished` to `isFinished` --- index.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/index.js b/index.js index 5314058..c8b8291 100644 --- a/index.js +++ b/index.js @@ -23,12 +23,12 @@ export default async function pMap( const errors = []; const iterator = iterable[Symbol.iterator](); const pendingIndexes = new Set(); - let finished = false; + let isFinished = false; let isIterableDone = false; let currentIndex = 0; const next = () => { - if (finished) { + if (isFinished) { return; } @@ -56,20 +56,20 @@ export default async function pMap( try { const element = await nextItem.value; - if (finished) { + if (isFinished) { return; } result[index] = await mapper(element, index); - if (finished) { + if (isFinished) { return; } pendingIndexes.delete(index); if (result[index] && result[index][stopSymbol]) { - finished = true; + isFinished = true; const stopConfig = result[index][stopSymbol]; result[index] = stopConfig.value; if (stopConfig.ongoingMappings.collapse) { @@ -86,7 +86,7 @@ export default async function pMap( } } catch (error) { if (stopOnError) { - finished = true; + isFinished = true; reject(error); } else { errors.push(error); From e70cd780209ae0bebee90dcd87741f3b5d09e53b Mon Sep 17 00:00:00 2001 From: Pedro Augusto de Paula Barbosa Date: Sat, 26 Jun 2021 15:22:20 -0300 Subject: [PATCH 3/9] Manual linting of examples --- index.d.ts | 6 +++--- readme.md | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/index.d.ts b/index.d.ts index 2c84a6a..0dcc87f 100644 --- a/index.d.ts +++ b/index.d.ts @@ -113,12 +113,12 @@ declare const pMap: { import pMap from 'p-map'; import got from 'got'; - const numbers = Array.from({ length: 2000 }).map((_, i) => i + 1); + const numbers = Array.from({length: 2000}).map((_, index) => index + 1); //=> [1, 2, ..., 1999, 2000] const mapper = async number => { if (number !== 404) { - const { transcript } = await got(`https://xkcd.com/${number}/info.0.json`).json(); + const {transcript} = await got(`https://xkcd.com/${number}/info.0.json`).json(); if (/unicorn/.test(transcript)) { console.log('Found a XKCD comic with an unicorn:', number); return pMap.stop(); @@ -126,7 +126,7 @@ declare const pMap: { } }; - await pMap(numbers, mapper, { concurrency: 50 }); + await pMap(numbers, mapper, {concurrency: 50}); //=> Found a XKCD comic with an unicorn: 948 ``` */ diff --git a/readme.md b/readme.md index 557d518..5a715bf 100644 --- a/readme.md +++ b/readme.md @@ -45,12 +45,12 @@ console.log(result); import pMap from 'p-map'; import got from 'got'; -const numbers = Array.from({ length: 2000 }).map((_, i) => i + 1); +const numbers = Array.from({length: 2000}).map((_, index) => index + 1); //=> [1, 2, ..., 1999, 2000] const mapper = async number => { if (number !== 404) { - const { transcript } = await got(`https://xkcd.com/${number}/info.0.json`).json(); + const {transcript} = await got(`https://xkcd.com/${number}/info.0.json`).json(); if (/unicorn/.test(transcript)) { console.log('Found a XKCD comic with an unicorn:', number); return pMap.stop(); @@ -58,7 +58,7 @@ const mapper = async number => { } }; -await pMap(numbers, mapper, { concurrency: 50 }); +await pMap(numbers, mapper, {concurrency: 50}); //=> Found a XKCD comic with an unicorn: 948 ``` From 941ed8b0850dfcf4a5afe76f306ead887fd11c3d Mon Sep 17 00:00:00 2001 From: Pedro Augusto de Paula Barbosa Date: Sat, 26 Jun 2021 15:25:42 -0300 Subject: [PATCH 4/9] Refactor index.d.ts away from `export const` --- index.d.ts | 66 +++++++++++++++++++++++++----------------------------- 1 file changed, 31 insertions(+), 35 deletions(-) diff --git a/index.d.ts b/index.d.ts index 0dcc87f..d557ca2 100644 --- a/index.d.ts +++ b/index.d.ts @@ -1,6 +1,6 @@ -declare const stop: unique symbol; +declare const stopSymbol: unique symbol; -export type StopSymbol = typeof stop; +export type StopSymbol = typeof stopSymbol; export interface Options { /** @@ -53,7 +53,7 @@ export interface StopOptions { } type BaseStopValueWrapper = { - [stop]: Required>; + [stopSymbol]: Required>; }; export type StopValueWrapper = NewElement extends any ? BaseStopValueWrapper : never; @@ -98,39 +98,35 @@ console.log(result); //=> ['https://sindresorhus.com/', 'https://avajs.dev/', 'https://github.com/'] ``` */ -declare const pMap: { - ( - input: Iterable, - mapper: Mapper, - options?: Options - ): Promise; +export default function pMap( + input: Iterable, + mapper: Mapper, + options?: Options +): Promise; - /** - Creates a special object that indicates to `pMap` that iteration must stop immediately. This object should just be returned from within the mapper (and not used directly for anything). - - @example - ``` - import pMap from 'p-map'; - import got from 'got'; - - const numbers = Array.from({length: 2000}).map((_, index) => index + 1); - //=> [1, 2, ..., 1999, 2000] - - const mapper = async number => { - if (number !== 404) { - const {transcript} = await got(`https://xkcd.com/${number}/info.0.json`).json(); - if (/unicorn/.test(transcript)) { - console.log('Found a XKCD comic with an unicorn:', number); - return pMap.stop(); - } - } - }; +/** +Creates a special object that indicates to `pMap` that iteration must stop immediately. This object should just be returned from within the mapper (and not used directly for anything). - await pMap(numbers, mapper, {concurrency: 50}); - //=> Found a XKCD comic with an unicorn: 948 - ``` - */ - stop: (options?: StopOptions) => StopValueWrapper; +@example +``` +import pMap from 'p-map'; +import got from 'got'; + +const numbers = Array.from({length: 2000}).map((_, index) => index + 1); +//=> [1, 2, ..., 1999, 2000] + +const mapper = async number => { + if (number !== 404) { + const {transcript} = await got(`https://xkcd.com/${number}/info.0.json`).json(); + if (/unicorn/.test(transcript)) { + console.log('Found a XKCD comic with an unicorn:', number); + return pMap.stop(); + } + } }; -export default pMap; +await pMap(numbers, mapper, {concurrency: 50}); +//=> Found a XKCD comic with an unicorn: 948 +``` +*/ +export function stop(options?: StopOptions): StopValueWrapper; From 3dcb6d0a4a3aa39ee633ded132483879d1d2f03f Mon Sep 17 00:00:00 2001 From: Pedro Augusto de Paula Barbosa Date: Sat, 26 Jun 2021 15:26:05 -0300 Subject: [PATCH 5/9] Improve wording in readme --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 5a715bf..9c1cc7a 100644 --- a/readme.md +++ b/readme.md @@ -8,7 +8,7 @@ This is different from `Promise.all()` in that you can: * Control the concurrency * Decide whether or not to stop iterating when there's an error -* Stop iterating at any point (like `break` in standard loops) +* Stop iterating at any point (like `break` in `for` loops) ## Install From 90a42b046fad95d7b2c943a5c1604c4876537e4a Mon Sep 17 00:00:00 2001 From: Pedro Augusto de Paula Barbosa Date: Sat, 26 Jun 2021 15:40:37 -0300 Subject: [PATCH 6/9] Fix typo in variable name --- index.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/index.js b/index.js index c8b8291..8939b69 100644 --- a/index.js +++ b/index.js @@ -22,7 +22,7 @@ export default async function pMap( const result = []; const errors = []; const iterator = iterable[Symbol.iterator](); - const pendingIndexes = new Set(); + const pendingIndices = new Set(); let isFinished = false; let isIterableDone = false; let currentIndex = 0; @@ -39,7 +39,7 @@ export default async function pMap( if (nextItem.done) { isIterableDone = true; - if (pendingIndexes.size === 0) { + if (pendingIndices.size === 0) { if (!stopOnError && errors.length > 0) { reject(new AggregateError(errors)); } else { @@ -50,7 +50,7 @@ export default async function pMap( return; } - pendingIndexes.add(index); + pendingIndices.add(index); (async () => { try { @@ -66,7 +66,7 @@ export default async function pMap( return; } - pendingIndexes.delete(index); + pendingIndices.delete(index); if (result[index] && result[index][stopSymbol]) { isFinished = true; @@ -75,7 +75,7 @@ export default async function pMap( if (stopConfig.ongoingMappings.collapse) { resolve(result.flat(0)); } else { - for (const pendingIndex of pendingIndexes) { + for (const pendingIndex of pendingIndices) { result[pendingIndex] = stopConfig.ongoingMappings.fillWith; } @@ -90,7 +90,7 @@ export default async function pMap( reject(error); } else { errors.push(error); - pendingIndexes.delete(index); + pendingIndices.delete(index); next(); } } From 31be4be768d10c7e7b91f255e43cb683f585660b Mon Sep 17 00:00:00 2001 From: Pedro Augusto de Paula Barbosa Date: Sat, 26 Jun 2021 15:41:15 -0300 Subject: [PATCH 7/9] Revert "Refactor index.d.ts away from `export const`" This reverts commit 941ed8b0850dfcf4a5afe76f306ead887fd11c3d. --- index.d.ts | 66 +++++++++++++++++++++++++++++------------------------- 1 file changed, 35 insertions(+), 31 deletions(-) diff --git a/index.d.ts b/index.d.ts index d557ca2..0dcc87f 100644 --- a/index.d.ts +++ b/index.d.ts @@ -1,6 +1,6 @@ -declare const stopSymbol: unique symbol; +declare const stop: unique symbol; -export type StopSymbol = typeof stopSymbol; +export type StopSymbol = typeof stop; export interface Options { /** @@ -53,7 +53,7 @@ export interface StopOptions { } type BaseStopValueWrapper = { - [stopSymbol]: Required>; + [stop]: Required>; }; export type StopValueWrapper = NewElement extends any ? BaseStopValueWrapper : never; @@ -98,35 +98,39 @@ console.log(result); //=> ['https://sindresorhus.com/', 'https://avajs.dev/', 'https://github.com/'] ``` */ -export default function pMap( - input: Iterable, - mapper: Mapper, - options?: Options -): Promise; +declare const pMap: { + ( + input: Iterable, + mapper: Mapper, + options?: Options + ): Promise; -/** -Creates a special object that indicates to `pMap` that iteration must stop immediately. This object should just be returned from within the mapper (and not used directly for anything). - -@example -``` -import pMap from 'p-map'; -import got from 'got'; - -const numbers = Array.from({length: 2000}).map((_, index) => index + 1); -//=> [1, 2, ..., 1999, 2000] - -const mapper = async number => { - if (number !== 404) { - const {transcript} = await got(`https://xkcd.com/${number}/info.0.json`).json(); - if (/unicorn/.test(transcript)) { - console.log('Found a XKCD comic with an unicorn:', number); - return pMap.stop(); + /** + Creates a special object that indicates to `pMap` that iteration must stop immediately. This object should just be returned from within the mapper (and not used directly for anything). + + @example + ``` + import pMap from 'p-map'; + import got from 'got'; + + const numbers = Array.from({length: 2000}).map((_, index) => index + 1); + //=> [1, 2, ..., 1999, 2000] + + const mapper = async number => { + if (number !== 404) { + const {transcript} = await got(`https://xkcd.com/${number}/info.0.json`).json(); + if (/unicorn/.test(transcript)) { + console.log('Found a XKCD comic with an unicorn:', number); + return pMap.stop(); + } } - } + }; + + await pMap(numbers, mapper, {concurrency: 50}); + //=> Found a XKCD comic with an unicorn: 948 + ``` + */ + stop: (options?: StopOptions) => StopValueWrapper; }; -await pMap(numbers, mapper, {concurrency: 50}); -//=> Found a XKCD comic with an unicorn: 948 -``` -*/ -export function stop(options?: StopOptions): StopValueWrapper; +export default pMap; From be4607573f3589fe270f2e131eaaeb62ae863142 Mon Sep 17 00:00:00 2001 From: Pedro Augusto de Paula Barbosa Date: Sat, 26 Jun 2021 15:47:59 -0300 Subject: [PATCH 8/9] Improve description of the `stop` function --- index.d.ts | 2 +- readme.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/index.d.ts b/index.d.ts index 0dcc87f..6a7ea42 100644 --- a/index.d.ts +++ b/index.d.ts @@ -106,7 +106,7 @@ declare const pMap: { ): Promise; /** - Creates a special object that indicates to `pMap` that iteration must stop immediately. This object should just be returned from within the mapper (and not used directly for anything). + Creates a special value that should be returned from `mapper` in order to immediately stop iteration. @example ``` diff --git a/readme.md b/readme.md index 9c1cc7a..ea4a120 100644 --- a/readme.md +++ b/readme.md @@ -101,7 +101,7 @@ When set to `false`, instead of stopping when a promise rejects, it will wait fo ### pMap.stop(options?) -Creates a special object that indicates to `pMap` that iteration must stop immediately. This object should just be returned from within the mapper (and not used directly for anything). +Creates a special value that should be returned from `mapper` in order to immediately stop iteration. #### options From 02cc500ebd09b34eb6a682b254cdc5cf4e57ddfe Mon Sep 17 00:00:00 2001 From: Pedro Augusto de Paula Barbosa Date: Sat, 26 Jun 2021 15:54:09 -0300 Subject: [PATCH 9/9] Undo change moved to another PR --- index.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/index.js b/index.js index 8939b69..0ab04f9 100644 --- a/index.js +++ b/index.js @@ -55,11 +55,6 @@ export default async function pMap( (async () => { try { const element = await nextItem.value; - - if (isFinished) { - return; - } - result[index] = await mapper(element, index); if (isFinished) {