Last active
February 5, 2017 11:16
-
-
Save shotamatsuda/f54d2106d4d603ca38d9e95e9deb34d2 to your computer and use it in GitHub Desktop.
Promise based semaphore
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
import { Namespace } from '../core/Namespace' | |
export const internal = Namespace('Semaphore') | |
class Task { | |
constructor(semaphore, callback) { | |
const promises = [ | |
new Promise((resolve, reject) => { | |
this.resolve = resolve | |
this.reject = reject | |
}), | |
new Promise(resolve => { | |
this.let = resolve | |
}).then(() => { | |
callback(this.resolve, this.reject) | |
}), | |
] | |
this.promise = Promise.all(promises) | |
.then(values => { | |
semaphore.signal() | |
return values[0] | |
}, reason => { | |
semaphore.signal() | |
return Promise.reject(reason) | |
}) | |
} | |
} | |
export class Semaphore { | |
constructor(capacity) { | |
const scope = internal(this) | |
scope.capacity = capacity | |
scope.available = capacity | |
scope.queue = [] | |
} | |
wait(callback) { | |
const scope = internal(this) | |
const task = new Task(this, callback) | |
if (scope.available === 0) { | |
scope.queue.push(task) | |
} else { | |
--scope.available | |
task.let() | |
} | |
return task.promise | |
} | |
signal() { | |
const scope = internal(this) | |
if (scope.queue.length === 0) { | |
++scope.available | |
} else { | |
scope.queue.shift().let() | |
} | |
} | |
// Properties | |
get capacity() { | |
const scope = internal(this) | |
return scope.capacity | |
} | |
get available() { | |
const scope = internal(this) | |
return scope.available | |
} | |
} |
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
import { Semaphore } from 'core/Semaphore' | |
const should = require('should') | |
describe('Semaphore', () => { | |
it('runs tasks below or equal its capacity', () => { | |
const semaphore = new Semaphore(10) | |
let count = 0 | |
for (let i = 0; i < 1000; ++i) { | |
semaphore.wait((resolve, reject) => { | |
++count | |
count.should.belowOrEqual(10) | |
setTimeout(() => { | |
--count | |
count.should.belowOrEqual(10) | |
resolve() | |
}, 100) | |
}) | |
} | |
}) | |
}) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment