-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Adding limited concurrency. * Adding the ability to control the delay * Added some tests which verify concurrency limits. * Getting ready to release.
- Loading branch information
1 parent
cfdf0a3
commit 1a6ed7f
Showing
8 changed files
with
168 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
{ | ||
"MD013": false, | ||
"MD024": { | ||
"siblings_only": true | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,5 @@ | ||
module.exports = { | ||
...require('./src/protected-promise'), | ||
...require('./src/coalesce-promises'), | ||
...require('./src/limited-concurrency'), | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
// Test the limitedConcurrency function | ||
const { limitedConcurrency } = require('../limited-concurrency') | ||
|
||
// This function waits three seconds and then resolves with `true` | ||
const waitThreeSeconds = () => | ||
new Promise((resolve) => setTimeout(resolve, 3000, true)) | ||
|
||
// Let's have things take 30 seconds for the timeout. | ||
jest.setTimeout(30000) | ||
|
||
describe('limitedConcurrency', () => { | ||
test('One promise at a time', async () => { | ||
const promises = [ | ||
waitThreeSeconds, | ||
waitThreeSeconds, | ||
waitThreeSeconds, | ||
waitThreeSeconds, | ||
waitThreeSeconds, | ||
] | ||
const results = await limitedConcurrency(promises, 3) | ||
expect(results).toHaveLength(5) | ||
expect(promises).toHaveLength(5) | ||
}) | ||
|
||
test('Verify that limitedConcurrency() actually limits concurrency', async () => { | ||
let totalRunCount = 0 | ||
|
||
// This function gives us a closure which returns a promise that increments the counter. | ||
const trackablePromise = () => () => | ||
Promise.resolve() | ||
.then(() => (totalRunCount += 1)) | ||
.then(waitThreeSeconds) | ||
|
||
// Make a list of five promises | ||
const promises = [ | ||
trackablePromise(), | ||
trackablePromise(), | ||
trackablePromise(), | ||
trackablePromise(), | ||
trackablePromise(), | ||
] | ||
|
||
// Run those promises with a concurrency limit of 3 | ||
const results = limitedConcurrency(promises, 3) | ||
|
||
// Wait a brief period of time to let the promises get started | ||
await new Promise((resolve) => setTimeout(resolve, 50)) | ||
|
||
// We've been less than 3 seconds since we started the promises, so we should have exactly 3 promises running, and the `totalRunCount` should now equal 3. | ||
expect(totalRunCount).toBe(3) | ||
|
||
// Wrap up the rest of the promises | ||
await results | ||
|
||
// Make sure that all of the promises did eventually run. | ||
expect(totalRunCount).toBe(5) | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
const { resolve } = require('bluebird') | ||
const { coalescePromises } = require('./coalesce-promises') | ||
const { protectPromise } = require('./protected-promise') | ||
|
||
const SleepyTimeDelay = 250 | ||
const sleepyTime = (sleepDelay) => | ||
new Promise((resolve) => setTimeout(resolve, sleepDelay, true)) | ||
|
||
/** | ||
* A function which allows for concurrent running of promises, but limits the number of concurrent promises. | ||
* @param {Array<Function>} promiseFunctions - An array of functions to run, all of which return a promise. | ||
* @param {Number} limit - The number of promises to run concurrently. | ||
* @param {Number} sleepDelay - The number of milliseconds to wait between limit checks. | ||
* @returns {Promise} - A promise which resolves when all promises have resolved. | ||
*/ | ||
const limitedConcurrency = async ( | ||
promises = [], | ||
limit = 1, | ||
sleepDelay = SleepyTimeDelay, | ||
) => { | ||
let currentlyRunning = 0 | ||
const leftToRun = [...promises] | ||
const finished = [] | ||
|
||
while (leftToRun.length > 0) { | ||
if (currentlyRunning < limit) { | ||
currentlyRunning += 1 | ||
finished.push( | ||
protectPromise(leftToRun.shift()()).then((result) => { | ||
currentlyRunning -= 1 | ||
return Promise.resolve(result) | ||
}), | ||
) | ||
} else { | ||
await sleepyTime(sleepDelay) | ||
} | ||
} | ||
|
||
return Promise.all(finished) | ||
} | ||
|
||
module.exports = { | ||
limitedConcurrency, | ||
} |