Skip to content

Commit

Permalink
Fixed map, filter and foreach behavior in case of exception
Browse files Browse the repository at this point in the history
  • Loading branch information
nicolas-van committed Dec 15, 2020
1 parent 72439b2 commit d55a373
Show file tree
Hide file tree
Showing 11 changed files with 97 additions and 1 deletion.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Changelog

## to be released

* Fixed map, filter and forEach behavior in case of exception. Pending tasks were not cancelled in case of exception and the
behavior in that case was not documented as opposed to other functions.

## 1.0.0

First version.
2 changes: 2 additions & 0 deletions src/filter.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import filterLimit from './filterLimit.mjs'
* The calls to `iteratee` will perform in parallel, but the results array will be in the same order
* than the original.
*
* If any of the calls to iteratee throws an exception the returned promised will be rejected.
*
* @param {Iterable} iterable An iterable object.
* @param {Function} iteratee A function that will be called with each member of `iterable`. It will receive
* three arguments:
Expand Down
3 changes: 3 additions & 0 deletions src/filterLimit.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ import assert from 'nanoassert'
* The calls to `iteratee` will perform in parallel, up to the concurrency limit, but the results array will be
* in the same order than the original.
*
* If any of the calls to iteratee throws an exception the returned promised will be rejected and the remaining
* pending tasks will be cancelled.
*
* @param {Iterable} iterable An iterable object.
* @param {Function} iteratee A function that will be called with each member of `iterable`. It will receive
* three arguments:
Expand Down
3 changes: 3 additions & 0 deletions src/filterSeries.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ import filterLimit from './filterLimit.mjs'
* The calls to `iteratee` will perform sequentially and the results array will be in the same order
* than the original.
*
* If any of the calls to iteratee throws an exception the returned promised will be rejected and the remaining
* pending tasks will be cancelled.
*
* @param {Iterable} iterable An iterable object.
* @param {Function} iteratee A function that will be called with each member of the iterable. It will receive
* three arguments:
Expand Down
2 changes: 2 additions & 0 deletions src/forEach.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import forEachLimit from './forEachLimit.mjs'
*
* Multiple calls to `iteratee` will be performed in parallel.
*
* If any of the calls to iteratee throws an exception the returned promised will be rejected.
*
* @param {Iterable} iterable An iterable object.
* @param {Function} iteratee A function that will be called with each member of the iterable. It will receive
* three arguments:
Expand Down
3 changes: 3 additions & 0 deletions src/forEachLimit.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ import mapLimit from './mapLimit.mjs'
*
* Multiple calls to `iteratee` will be performed in parallel, up to the concurrency limit.
*
* If any of the calls to iteratee throws an exception the returned promised will be rejected and the remaining
* pending tasks will be cancelled.
*
* @param {Iterable} iterable An iterable object.
* @param {Function} iteratee A function that will be called with each member of the iterable. It will receive
* three arguments:
Expand Down
3 changes: 3 additions & 0 deletions src/forEachSeries.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ import forEachLimit from './forEachLimit.mjs'
*
* Multiple calls to `iteratee` will be performed sequentially.
*
* If any of the calls to iteratee throws an exception the returned promised will be rejected and the remaining
* pending tasks will be cancelled.
*
* @param {Iterable} iterable An iterable object.
* @param {Function} iteratee A function that will be called with each member of the iterable. It will receive
* three arguments:
Expand Down
2 changes: 2 additions & 0 deletions src/map.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import mapLimit from './mapLimit.mjs'
*
* Multiple calls to `iteratee` will be performed in parallel.
*
* If any of the calls to iteratee throws an exception the returned promised will be rejected.
*
* @param {Iterable} iterable An iterable object.
* @param {Function} iteratee A function that will be called with each member of the iterable. It will receive
* three arguments:
Expand Down
9 changes: 8 additions & 1 deletion src/mapLimit.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ import assert from 'nanoassert'
*
* Multiple calls to `iteratee` will be performed in parallel, up to the concurrency limit.
*
* If any of the calls to iteratee throws an exception the returned promised will be rejected and the remaining
* pending tasks will be cancelled.
*
* @param {Iterable} iterable An iterable object.
* @param {Function} iteratee A function that will be called with each member of the iterable. It will receive
* three arguments:
Expand Down Expand Up @@ -43,7 +46,11 @@ async function mapLimit (iterable, iteratee, concurrency) {
}))
i += 1
}
return Promise.all(promises)
try {
return await Promise.all(promises)
} finally {
queue.cancelAllPending()
}
}

export default mapLimit
63 changes: 63 additions & 0 deletions src/mapLimit.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,66 @@ test('mapLimit index & iterable', async () => {
}, 2)
expect(res).toEqual([0, 2, 4, 6, 8, 10])
})

test('mapLimit one exception', async () => {
const arr = _.range(3)
const called = {}
arr.forEach((v) => { called[v] = 0 })
try {
await mapLimit(arr, async (x) => {
called[x] += 1
await sleepPrecise(10)
if (x === 1) {
throw new Error('test')
}
return x * 2
}, 1)
expect(false).toBe(true)
} catch (e) {
expect(e.message).toBe('test')
}
await sleepPrecise(100)
expect(called[0]).toBe(1)
expect(called[1]).toBe(1)
expect(called[2]).toBe(0)
})

test('mapLimit all exception c 1', async () => {
const arr = _.range(3)
const called = {}
arr.forEach((v) => { called[v] = 0 })
try {
await mapLimit(arr, async (x) => {
called[x] += 1
await sleepPrecise(10)
throw new Error('test')
}, 1)
expect(false).toBe(true)
} catch (e) {
expect(e.message).toBe('test')
}
await sleepPrecise(100)
expect(called[0]).toBe(1)
expect(called[1]).toBe(0)
expect(called[2]).toBe(0)
})

test('mapLimit all exception c 2', async () => {
const arr = _.range(3)
const called = {}
arr.forEach((v) => { called[v] = 0 })
try {
await mapLimit(arr, async (x) => {
called[x] += 1
await sleepPrecise(10)
throw new Error('test')
}, 2)
expect(false).toBe(true)
} catch (e) {
expect(e.message).toBe('test')
}
await sleepPrecise(100)
expect(called[0]).toBe(1)
expect(called[1]).toBe(1)
expect(called[2]).toBe(0)
})
3 changes: 3 additions & 0 deletions src/mapSeries.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ import mapLimit from './mapLimit.mjs'
*
* Multiple calls to `iteratee` will be performed sequentially.
*
* If any of the calls to iteratee throws an exception the returned promised will be rejected and the remaining
* pending tasks will be cancelled.
*
* @param {Iterable} iterable An iterable object.
* @param {Function} iteratee A function that will be called with each member of the iterable. It will receive
* three arguments:
Expand Down

0 comments on commit d55a373

Please sign in to comment.