(function (root, factory) {
if (typeof define === 'function' && define.amd) {
define([], factory)
} else if (typeof exports === 'object') {
module.exports = factory()
} else {
root.PromisePool = factory()
root.promisePool = root.PromisePool
}
})(this, function () {
'use strict'
var EventTarget = function () {
this._listeners = {}
}
EventTarget.prototype.addEventListener = function (type, listener) {
this._listeners[type] = this._listeners[type] || []
if (this._listeners[type].indexOf(listener) < 0) {
this._listeners[type].push(listener)
}
}
EventTarget.prototype.removeEventListener = function (type, listener) {
if (this._listeners[type]) {
var p = this._listeners[type].indexOf(listener)
if (p >= 0) {
this._listeners[type].splice(p, 1)
}
}
}
EventTarget.prototype.dispatchEvent = function (evt) {
if (this._listeners[evt.type] && this._listeners[evt.type].length) {
var listeners = this._listeners[evt.type].slice()
for (var i = 0, l = listeners.length; i < l; ++i) {
listeners[i].call(this, evt)
}
}
}
var isGenerator = function (func) {
return (typeof func.constructor === 'function' &&
func.constructor.name === 'GeneratorFunction')
}
var functionToIterator = function (func) {
return {
next: function () {
var promise = func()
return promise ? {value: promise} : {done: true}
}
}
}
var promiseToIterator = function (promise) {
var called = false
return {
next: function () {
if (called) {
return {done: true}
}
called = true
return {value: promise}
}
}
}
var toIterator = function (obj, Promise) {
var type = typeof obj
if (type === 'object') {
if (typeof obj.next === 'function') {
return obj
}
if (typeof obj.then === 'function') {
return promiseToIterator(obj)
}
}
if (type === 'function') {
return isGenerator(obj) ? obj() : functionToIterator(obj)
}
return promiseToIterator(Promise.resolve(obj))
}
var PromisePoolEvent = function (target, type, data) {
this.target = target
this.type = type
this.data = data
}
var PromisePool = function (source, concurrency, options) {
EventTarget.call(this)
if (typeof concurrency !== 'number' ||
Math.floor(concurrency) !== concurrency ||
concurrency < 1) {
throw new Error('Invalid concurrency')
}
this._concurrency = concurrency
this._options = options || {}
this._options.promise = this._options.promise || Promise
this._iterator = toIterator(source, this._options.promise)
this._done = false
this._size = 0
this._promise = null
this._callbacks = null
}
PromisePool.prototype = new EventTarget()
PromisePool.prototype.constructor = PromisePool
PromisePool.prototype.concurrency = function (value) {
if (typeof value !== 'undefined') {
this._concurrency = value
if (this.active()) {
this._proceed()
}
}
return this._concurrency
}
PromisePool.prototype.size = function () {
return this._size
}
PromisePool.prototype.active = function () {
return !!this._promise
}
PromisePool.prototype.promise = function () {
return this._promise
}
PromisePool.prototype.start = function () {
var that = this
var Promise = this._options.promise
this._promise = new Promise(function (resolve, reject) {
that._callbacks = {
reject: reject,
resolve: resolve
}
that._proceed()
})
return this._promise
}
PromisePool.prototype._fireEvent = function (type, data) {
this.dispatchEvent(new PromisePoolEvent(this, type, data))
}
PromisePool.prototype._settle = function (error) {
if (error) {
this._callbacks.reject(error)
} else {
this._callbacks.resolve()
}
this._promise = null
this._callbacks = null
}
PromisePool.prototype._onPooledPromiseFulfilled = function (promise, result) {
this._size--
if (this.active()) {
this._fireEvent('fulfilled', {
promise: promise,
result: result
})
this._proceed()
}
}
PromisePool.prototype._onPooledPromiseRejected = function (promise, error) {
this._size--
if (this.active()) {
this._fireEvent('rejected', {
promise: promise,
error: error
})
this._settle(error || new Error('Unknown error'))
}
}
PromisePool.prototype._trackPromise = function (promise) {
var that = this
promise
.then(function (result) {
that._onPooledPromiseFulfilled(promise, result)
}, function (error) {
that._onPooledPromiseRejected(promise, error)
})['catch'](function (err) {
that._settle(new Error('Promise processing failed: ' + err))
})
}
PromisePool.prototype._proceed = function () {
if (!this._done) {
var result = { done: false }
while (this._size < this._concurrency &&
!(result = this._iterator.next()).done) {
this._size++
this._trackPromise(result.value)
}
this._done = (result === null || !!result.done)
}
if (this._done && this._size === 0) {
this._settle()
}
}
PromisePool.PromisePoolEvent = PromisePoolEvent
PromisePool.PromisePool = PromisePool
return PromisePool
})