216 lines
5.4 KiB
JavaScript
216 lines
5.4 KiB
JavaScript
(function (root, factory) {
|
|
/* istanbul ignore next */
|
|
if (typeof define === 'function' && define.amd) {
|
|
define([], factory)
|
|
} else if (typeof exports === 'object') {
|
|
module.exports = factory()
|
|
} else {
|
|
root.PromisePool = factory()
|
|
// Legacy API
|
|
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
|
|
}
|
|
/* istanbul ignore else */
|
|
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
|
|
// Legacy API
|
|
PromisePool.PromisePool = PromisePool
|
|
|
|
return PromisePool
|
|
})
|