Initial commit: New MoreminiMore website with fresh design
This commit is contained in:
9
node_modules/promise-limit/.eslintrc
generated
vendored
Normal file
9
node_modules/promise-limit/.eslintrc
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"extends": [
|
||||
"standard",
|
||||
"plugin:es5/no-es2015"
|
||||
],
|
||||
"rules": {
|
||||
"no-console": "error"
|
||||
}
|
||||
}
|
||||
3
node_modules/promise-limit/.travis.yml
generated
vendored
Normal file
3
node_modules/promise-limit/.travis.yml
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
- stable
|
||||
97
node_modules/promise-limit/README.md
generated
vendored
Normal file
97
node_modules/promise-limit/README.md
generated
vendored
Normal file
@@ -0,0 +1,97 @@
|
||||
promise-limit [](https://www.npmjs.com/package/promise-limit) [](https://www.npmjs.com/package/promise-limit) [](https://travis-ci.org/featurist/promise-limit)
|
||||
===
|
||||
|
||||
Limit outstanding calls to promise returning functions, or a semaphore for promises. You might want to do this to reduce load on external services, or reduce memory usage when processing large batches of jobs.
|
||||
|
||||
```sh
|
||||
npm install promise-limit
|
||||
```
|
||||
|
||||
```js
|
||||
var promiseLimit = require('promise-limit')
|
||||
|
||||
var limit = promiseLimit(2)
|
||||
|
||||
var jobs = ['a', 'b', 'c', 'd', 'e']
|
||||
|
||||
Promise.all(jobs.map((name) => {
|
||||
return limit(() => job(name))
|
||||
})).then(results => {
|
||||
console.log()
|
||||
console.log('results:', results)
|
||||
})
|
||||
|
||||
function job (name) {
|
||||
var text = `job ${name}`
|
||||
console.log('started', text)
|
||||
|
||||
return new Promise(function (resolve) {
|
||||
setTimeout(() => {
|
||||
console.log(' ', text, 'finished')
|
||||
resolve(text)
|
||||
}, 100)
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
will output:
|
||||
|
||||
```
|
||||
started job a
|
||||
started job b
|
||||
job a finished
|
||||
job b finished
|
||||
started job c
|
||||
started job d
|
||||
job c finished
|
||||
job d finished
|
||||
started job e
|
||||
job e finished
|
||||
|
||||
results: [ 'job a', 'job b', 'job c', 'job d', 'job e' ]
|
||||
```
|
||||
|
||||
API
|
||||
---
|
||||
|
||||
```js
|
||||
var promiseLimit = require('promise-limit')
|
||||
|
||||
promiseLimit(concurrency?: Number) -> limit
|
||||
```
|
||||
|
||||
Returns a function that can be used to wrap promise returning functions, limiting them to `concurrency` outstanding calls.
|
||||
|
||||
- `concurrency` the concurrency, i.e. 1 will limit calls to one at a time, effectively in sequence or serial. 2 will allow two at a time, etc. 0 or `undefined` specify no limit, and all calls will be run in parallel.
|
||||
|
||||
limit
|
||||
=====
|
||||
|
||||
```js
|
||||
limit(fn: () -> Promise<T>) -> Promise<T>
|
||||
```
|
||||
|
||||
A function that limits calls to `fn`, based on `concurrency` above. Returns a promise that resolves or rejects the same value or error as `fn`. All functions are executed in the same order in which they were passed to `limit`. `fn` must return a promise.
|
||||
|
||||
* `fn` a function that is called with no arguments and returns a promise. You can pass arguments to your function by putting it inside another function, i.e. `() -> myfunc(a, b, c)`.
|
||||
|
||||
limit.map
|
||||
=========
|
||||
|
||||
```js
|
||||
limit.map(items: [T], mapper: (T) -> Promise<R>) -> Promise<[R]>
|
||||
```
|
||||
|
||||
Maps an array of items using `mapper`, but limiting the number of concurrent calls to `mapper` with the `concurrency` of `limit`. If at least one call to `mapper` returns a rejected promise, the result of `map` is a the same rejected promise, and no further calls to `mapper` are made.
|
||||
|
||||
limit.queue
|
||||
===========
|
||||
|
||||
```js
|
||||
limit.queue: Number
|
||||
```
|
||||
|
||||
Returns the queue length, the number of jobs that are waiting to be started. You could use this to throttle incoming jobs, so the queue doesn't overwhealm the available memory - for e.g. `pause()` a stream.
|
||||
|
||||
## We're Hiring!
|
||||
Featurist provides full stack, feature driven development teams. Want to join us? Check out [our career opportunities](https://www.featurist.co.uk/careers/).
|
||||
22
node_modules/promise-limit/example/example.ts
generated
vendored
Normal file
22
node_modules/promise-limit/example/example.ts
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
import * as promiseLimit from '../'
|
||||
|
||||
const limit = promiseLimit<string>(2)
|
||||
var jobs = ['a', 'b', 'c', 'd', 'e']
|
||||
|
||||
Promise.all(jobs.map((name) => {
|
||||
return limit(() => job(name))
|
||||
})).then(results => {
|
||||
console.log('\nresults:', results)
|
||||
})
|
||||
|
||||
function job (name): Promise<string> {
|
||||
var text = `job ${name}`
|
||||
console.log('started', text)
|
||||
|
||||
return new Promise(function (resolve) {
|
||||
setTimeout(() => {
|
||||
console.log(' ', text, 'finished')
|
||||
resolve(text)
|
||||
}, 100)
|
||||
})
|
||||
}
|
||||
45
node_modules/promise-limit/index.d.ts
generated
vendored
Normal file
45
node_modules/promise-limit/index.d.ts
generated
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
export = limitFactory
|
||||
|
||||
declare namespace limitFactory {}
|
||||
|
||||
/**
|
||||
* Returns a function that can be used to wrap promise returning functions,
|
||||
* limiting them to concurrency outstanding calls.
|
||||
* @param concurrency the concurrency, i.e. 1 will limit calls to one at a
|
||||
* time, effectively in sequence or serial. 2 will allow two at a time, etc.
|
||||
* 0 or undefined specify no limit, and all calls will be run in parallel.
|
||||
*/
|
||||
declare function limitFactory<T>(concurrency?: number): limit<T>
|
||||
|
||||
declare type limit<T> = limitFunc<T> & limitInterface<T>
|
||||
|
||||
declare interface limitInterface<T> {
|
||||
/**
|
||||
* Maps an array of items using mapper, but limiting the number of concurrent
|
||||
* calls to mapper with the concurrency of limit. If at least one call to
|
||||
* mapper returns a rejected promise, the result of map is a the same rejected
|
||||
* promise, and no further calls to mapper are made.
|
||||
* @param items any items
|
||||
* @param mapper iterator
|
||||
*/
|
||||
map<U>(items: ReadonlyArray<T>, mapper: (value: T) => Promise<U>): Promise<U[]>
|
||||
|
||||
/**
|
||||
* Returns the queue length, the number of jobs that are waiting to be started.
|
||||
* You could use this to throttle incoming jobs, so the queue doesn't
|
||||
* overwhealm the available memory - for e.g. pause() a stream.
|
||||
*/
|
||||
queue: number
|
||||
}
|
||||
|
||||
/**
|
||||
* A function that limits calls to fn, based on concurrency above. Returns a
|
||||
* promise that resolves or rejects the same value or error as fn. All functions
|
||||
* are executed in the same order in which they were passed to limit. fn must
|
||||
* return a promise.
|
||||
* @param fn a function that is called with no arguments and returns a promise.
|
||||
* You can pass arguments to your function by putting it inside another function,
|
||||
* i.e. `() -> myfunc(a, b, c)`.
|
||||
*/
|
||||
declare type limitFunc<T> = (fn: () => Promise<T>) => Promise<T>
|
||||
|
||||
88
node_modules/promise-limit/index.js
generated
vendored
Normal file
88
node_modules/promise-limit/index.js
generated
vendored
Normal file
@@ -0,0 +1,88 @@
|
||||
function limiter (count) {
|
||||
var outstanding = 0
|
||||
var jobs = []
|
||||
|
||||
function remove () {
|
||||
outstanding--
|
||||
|
||||
if (outstanding < count) {
|
||||
dequeue()
|
||||
}
|
||||
}
|
||||
|
||||
function dequeue () {
|
||||
var job = jobs.shift()
|
||||
semaphore.queue = jobs.length
|
||||
|
||||
if (job) {
|
||||
run(job.fn).then(job.resolve).catch(job.reject)
|
||||
}
|
||||
}
|
||||
|
||||
function queue (fn) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
jobs.push({fn: fn, resolve: resolve, reject: reject})
|
||||
semaphore.queue = jobs.length
|
||||
})
|
||||
}
|
||||
|
||||
function run (fn) {
|
||||
outstanding++
|
||||
try {
|
||||
return Promise.resolve(fn()).then(function (result) {
|
||||
remove()
|
||||
return result
|
||||
}, function (error) {
|
||||
remove()
|
||||
throw error
|
||||
})
|
||||
} catch (err) {
|
||||
remove()
|
||||
return Promise.reject(err)
|
||||
}
|
||||
}
|
||||
|
||||
var semaphore = function (fn) {
|
||||
if (outstanding >= count) {
|
||||
return queue(fn)
|
||||
} else {
|
||||
return run(fn)
|
||||
}
|
||||
}
|
||||
|
||||
return semaphore
|
||||
}
|
||||
|
||||
function map (items, mapper) {
|
||||
var failed = false
|
||||
|
||||
var limit = this
|
||||
|
||||
return Promise.all(items.map(function () {
|
||||
var args = arguments
|
||||
return limit(function () {
|
||||
if (!failed) {
|
||||
return mapper.apply(undefined, args).catch(function (e) {
|
||||
failed = true
|
||||
throw e
|
||||
})
|
||||
}
|
||||
})
|
||||
}))
|
||||
}
|
||||
|
||||
function addExtras (fn) {
|
||||
fn.queue = 0
|
||||
fn.map = map
|
||||
return fn
|
||||
}
|
||||
|
||||
module.exports = function (count) {
|
||||
if (count) {
|
||||
return addExtras(limiter(count))
|
||||
} else {
|
||||
return addExtras(function (fn) {
|
||||
return fn()
|
||||
})
|
||||
}
|
||||
}
|
||||
39
node_modules/promise-limit/package.json
generated
vendored
Normal file
39
node_modules/promise-limit/package.json
generated
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"name": "promise-limit",
|
||||
"version": "2.7.0",
|
||||
"description": "limits calls to functions that return promises",
|
||||
"main": "index.js",
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
"chai": "4.1.2",
|
||||
"chai-as-promised": "7.1.1",
|
||||
"eslint": "*",
|
||||
"eslint-config-standard": "11.0.0",
|
||||
"eslint-plugin-es5": "1.3.1",
|
||||
"eslint-plugin-import": "*",
|
||||
"eslint-plugin-node": "7.0.1",
|
||||
"eslint-plugin-promise": "3.8.0",
|
||||
"eslint-plugin-standard": "3.1.0",
|
||||
"fs-promise": "2.0.3",
|
||||
"lowscore": "1.17.0",
|
||||
"mocha": "*"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "mocha && eslint ."
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/featurist/promise-limit.git"
|
||||
},
|
||||
"keywords": [
|
||||
"limit",
|
||||
"promise",
|
||||
"semaphore"
|
||||
],
|
||||
"author": "Tim Macfarlane <timmacfarlane@gmail.com>",
|
||||
"license": "ISC",
|
||||
"bugs": {
|
||||
"url": "https://github.com/featurist/promise-limit/issues"
|
||||
},
|
||||
"homepage": "https://github.com/featurist/promise-limit#readme"
|
||||
}
|
||||
3
node_modules/promise-limit/test/max.js
generated
vendored
Normal file
3
node_modules/promise-limit/test/max.js
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
module.exports = function (numbers) {
|
||||
return Math.max.apply(null, numbers)
|
||||
}
|
||||
48
node_modules/promise-limit/test/memoryUsageSpec.js
generated
vendored
Normal file
48
node_modules/promise-limit/test/memoryUsageSpec.js
generated
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
/* eslint-env mocha */
|
||||
|
||||
var times = require('lowscore/times')
|
||||
var limiter = require('..')
|
||||
var expect = require('chai').expect
|
||||
var max = require('./max')
|
||||
|
||||
describe('promise-limit memory usage', function () {
|
||||
var memoryProfile
|
||||
|
||||
beforeEach(function () {
|
||||
memoryProfile = []
|
||||
})
|
||||
|
||||
function checkMemory () {
|
||||
var memory = process.memoryUsage().heapUsed
|
||||
memoryProfile.push(memory)
|
||||
}
|
||||
|
||||
function load (i) {
|
||||
checkMemory()
|
||||
return new Promise(function (resolve, reject) {
|
||||
setTimeout(resolve, 10)
|
||||
}).then(function () {
|
||||
checkMemory()
|
||||
var array = new Array(1024 * 1024)
|
||||
|
||||
return {
|
||||
i: i,
|
||||
array: array
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
it("doesn't use too much memory", function () {
|
||||
this.timeout(20000)
|
||||
|
||||
var limit = limiter(5)
|
||||
return Promise.all(times(1000, function (i) {
|
||||
return limit(function () {
|
||||
return load(i).then(function () {})
|
||||
})
|
||||
})).then(function () {
|
||||
var maxMemoryUsage = max(memoryProfile)
|
||||
expect(maxMemoryUsage).to.be.lessThan(2000 * 1024 * 1024)
|
||||
})
|
||||
})
|
||||
})
|
||||
270
node_modules/promise-limit/test/spec.js
generated
vendored
Normal file
270
node_modules/promise-limit/test/spec.js
generated
vendored
Normal file
@@ -0,0 +1,270 @@
|
||||
/* eslint-env mocha */
|
||||
|
||||
var times = require('lowscore/times')
|
||||
var range = require('lowscore/range')
|
||||
var limiter = require('..')
|
||||
var chai = require('chai')
|
||||
var expect = chai.expect
|
||||
chai.use(require('chai-as-promised'))
|
||||
var max = require('./max')
|
||||
|
||||
describe('promise-limit', function () {
|
||||
var output
|
||||
|
||||
beforeEach(function () {
|
||||
output = []
|
||||
})
|
||||
|
||||
function wait (text, n) {
|
||||
output.push('starting', text)
|
||||
|
||||
return new Promise(function (resolve) {
|
||||
setTimeout(resolve, n)
|
||||
}).then(function () {
|
||||
output.push('finished', text)
|
||||
return text
|
||||
})
|
||||
}
|
||||
|
||||
function expectMaxOutstanding (n) {
|
||||
var outstanding = 0
|
||||
|
||||
var outstandingOverTime = output.map(function (line) {
|
||||
if (line.match(/starting/)) {
|
||||
outstanding++
|
||||
} else if (line.match(/finished/)) {
|
||||
outstanding--
|
||||
}
|
||||
|
||||
return outstanding
|
||||
})
|
||||
|
||||
var maxOutstanding = max(outstandingOverTime)
|
||||
expect(maxOutstanding).to.equal(n)
|
||||
}
|
||||
|
||||
it('limits the number of outstanding calls to a function', function () {
|
||||
var limit = limiter(5)
|
||||
|
||||
return Promise.all(times(9, function (i) {
|
||||
return limit(function () {
|
||||
return wait('job ' + (i + 1), 100)
|
||||
})
|
||||
})).then(function () {
|
||||
expectMaxOutstanding(5)
|
||||
})
|
||||
})
|
||||
|
||||
it("doesn't limit if the number outstanding is the limit", function () {
|
||||
var limit = limiter(5)
|
||||
|
||||
return Promise.all(times(5, function (i) {
|
||||
return limit(function () {
|
||||
return wait('job ' + (i + 1), 100)
|
||||
})
|
||||
})).then(function () {
|
||||
expectMaxOutstanding(5)
|
||||
})
|
||||
})
|
||||
|
||||
it("doesn't limit if the number outstanding less than the limit", function () {
|
||||
var limit = limiter(5)
|
||||
|
||||
return Promise.all(times(4, function (i) {
|
||||
return limit(function () {
|
||||
return wait('job ' + (i + 1), 100)
|
||||
})
|
||||
})).then(function () {
|
||||
expectMaxOutstanding(4)
|
||||
})
|
||||
})
|
||||
|
||||
it('returns the results from each job', function () {
|
||||
var limit = limiter(5)
|
||||
|
||||
return Promise.all(times(9, function (i) {
|
||||
return limit(function () {
|
||||
return wait('job ' + (i + 1), 100)
|
||||
})
|
||||
})).then(function (results) {
|
||||
expect(results).to.eql(times(9, function (i) {
|
||||
return 'job ' + (i + 1)
|
||||
}))
|
||||
})
|
||||
})
|
||||
|
||||
it('returns a rejected promise if the function throws an error', function () {
|
||||
var limit = limiter(5)
|
||||
|
||||
var promise = limit(function () {
|
||||
throw new Error('uh oh')
|
||||
})
|
||||
expect(promise).to.be.a('promise')
|
||||
return promise.then(function () {
|
||||
throw new Error('the promise resolved, instead of rejecting')
|
||||
}).catch(function (err) {
|
||||
expect(String(err)).to.equal('Error: uh oh')
|
||||
})
|
||||
})
|
||||
|
||||
it('should fulfil or reject when the function fulfils or rejects', function () {
|
||||
var limit = limiter(2)
|
||||
|
||||
var numbers = [1, 2, 3, 4, 5, 6]
|
||||
|
||||
function rejectOdd (n) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
if (n % 2 === 0) {
|
||||
resolve(n + ' is even')
|
||||
} else {
|
||||
reject(new Error(n + ' is odd'))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return Promise.all(numbers.map(function (i) {
|
||||
return limit(function () {
|
||||
return rejectOdd(i)
|
||||
}).then(function (r) {
|
||||
return 'pass: ' + r
|
||||
}, function (e) {
|
||||
return 'fail: ' + e.message
|
||||
})
|
||||
})).then(function (results) {
|
||||
expect(results).to.eql([
|
||||
'fail: 1 is odd',
|
||||
'pass: 2 is even',
|
||||
'fail: 3 is odd',
|
||||
'pass: 4 is even',
|
||||
'fail: 5 is odd',
|
||||
'pass: 6 is even'
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
describe('no limit', function () {
|
||||
function expectNoLimit (limit) {
|
||||
return Promise.all(times(9, function (i) {
|
||||
return limit(function () { return wait('job ' + (i + 1), 100) })
|
||||
})).then(function () {
|
||||
expectMaxOutstanding(9)
|
||||
}).then(function () {
|
||||
return expectNoMapLimit(limit)
|
||||
})
|
||||
}
|
||||
|
||||
function expectNoMapLimit (limit) {
|
||||
return limit.map(range(0, 9), function (i) {
|
||||
return wait('job ' + (i + 1), 100)
|
||||
}).then(function () {
|
||||
expectMaxOutstanding(9)
|
||||
})
|
||||
}
|
||||
|
||||
it("doesn't limit if the limit is 0", function () {
|
||||
var limit = limiter(0)
|
||||
|
||||
return expectNoLimit(limit)
|
||||
})
|
||||
|
||||
it("doesn't limit if the limit is undefined", function () {
|
||||
var limit = limiter()
|
||||
|
||||
return expectNoLimit(limit)
|
||||
})
|
||||
})
|
||||
|
||||
describe('map', function () {
|
||||
function failsAt1 (num) {
|
||||
if (num === 1) return Promise.reject(new Error('rejecting number ' + num))
|
||||
else return Promise.resolve('accepting number ' + num)
|
||||
}
|
||||
|
||||
function resolvesAll (num) {
|
||||
return Promise.resolve('accepting number ' + num)
|
||||
}
|
||||
|
||||
it('returns all results when all are resolved', function () {
|
||||
var limit = limiter(2)
|
||||
|
||||
return limit.map([0, 1, 2, 3], resolvesAll).then(function (results) {
|
||||
expect(results).to.eql([
|
||||
'accepting number 0',
|
||||
'accepting number 1',
|
||||
'accepting number 2',
|
||||
'accepting number 3'
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
it('returns first failure when one fails', function () {
|
||||
var limit = limiter(2)
|
||||
|
||||
return expect(limit.map([0, 1, 2, 3], failsAt1)).to.be.rejectedWith('rejecting number 1')
|
||||
})
|
||||
|
||||
it('limiter can still be used after map with failure', function () {
|
||||
var limit = limiter(2)
|
||||
|
||||
return expect(limit.map([0, 1, 2, 3], failsAt1)).to.be.rejectedWith('rejecting number 1').then(function () {
|
||||
return limit.map([0, 1, 2, 3], resolvesAll).then(function (results) {
|
||||
expect(results).to.eql([
|
||||
'accepting number 0',
|
||||
'accepting number 1',
|
||||
'accepting number 2',
|
||||
'accepting number 3'
|
||||
])
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('large numbers of tasks failing', function () {
|
||||
context('when error thrown', function () {
|
||||
function alwaysFails (n) {
|
||||
throw new Error('argh: ' + n)
|
||||
}
|
||||
|
||||
it("doesn't exceed stack size", function () {
|
||||
var limit = limiter(2)
|
||||
|
||||
return expect(limit.map(range(0, 1000), alwaysFails)).to.be.rejectedWith('argh: 0')
|
||||
})
|
||||
})
|
||||
|
||||
context('when error rejected', function () {
|
||||
function alwaysFails (n) {
|
||||
return Promise.reject(new Error('argh: ' + n))
|
||||
}
|
||||
|
||||
it("doesn't exceed stack size", function () {
|
||||
var limit = limiter(2)
|
||||
|
||||
return expect(limit.map(range(0, 1000), alwaysFails)).to.be.rejectedWith('argh: 0')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('queue length', function () {
|
||||
it('updates the queue length when there are more jobs than there is concurrency', function () {
|
||||
var limit = limiter(2)
|
||||
|
||||
var one = limit(function () { return wait('one', 10) })
|
||||
expect(limit.queue).to.equal(0)
|
||||
var two = limit(function () { return wait('two', 20) })
|
||||
expect(limit.queue).to.equal(0)
|
||||
limit(function () { return wait('three', 100) })
|
||||
expect(limit.queue).to.equal(1)
|
||||
limit(function () { return wait('four', 100) })
|
||||
expect(limit.queue).to.equal(2)
|
||||
|
||||
return one.then(function () {
|
||||
expect(limit.queue).to.equal(1)
|
||||
|
||||
return two.then(function () {
|
||||
expect(limit.queue).to.equal(0)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user