Comparing version 0.1.10 to 0.2.0
247
lib/index.js
@@ -1,248 +0,9 @@ | ||
/** | ||
* Controls list of pending promises. | ||
*/ | ||
'use strict'; | ||
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); | ||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } | ||
var promiseFinally = require('promise.prototype.finally'); | ||
var Pendings = require('./pendings'); | ||
var Pending = require('./pending'); | ||
var TimeoutError = require('./timeout-error'); | ||
var Pendings = function () { | ||
/** | ||
* Constructor. | ||
* | ||
* @param {Object} [options] | ||
* @param {String} [options.idPrefix=''] prefix for generated IDs | ||
* @param {Number} [options.timeout=0] default timeout for all promises | ||
*/ | ||
function Pendings(options) { | ||
_classCallCheck(this, Pendings); | ||
options = options || {}; | ||
this._timeout = options.timeout || 0; | ||
this._idPrefix = options.idPrefix || ''; | ||
this._map = Object.create(null); | ||
} | ||
/** | ||
* Calls `fn` and returns new promise. `fn` gets generated unique `id` as parameter. | ||
* | ||
* @param {Function} fn | ||
* @param {Object} [options] | ||
* @param {Number} [options.timeout] custom timeout for particular promise | ||
* @returns {Promise} | ||
*/ | ||
_createClass(Pendings, [{ | ||
key: 'add', | ||
value: function add(fn, options) { | ||
var id = this.generateId(); | ||
return this.set(id, fn, options); | ||
} | ||
/** | ||
* Calls `fn` and returns new promise with specified `id`. | ||
* | ||
* @param {String} id | ||
* @param {Function} fn | ||
* @param {Object} [options] | ||
* @param {Number} [options.timeout] custom timeout for particular promise | ||
* @returns {Promise} | ||
*/ | ||
}, { | ||
key: 'set', | ||
value: function set(id, fn, options) { | ||
var _this = this; | ||
if (!this.has(id)) { | ||
var pending = this._map[id] = new Pending(); | ||
var timeout = this._getTimeout(options); | ||
var promise = pending.call(function () { | ||
return fn(id); | ||
}, timeout); | ||
pending.finalPromise = promiseFinally(promise, function () { | ||
return delete _this._map[id]; | ||
}); | ||
} | ||
return this.getPromise(id); | ||
} | ||
/** | ||
* Checks if pending promise with specified `id` exists. | ||
* | ||
* @param {String} id | ||
* @returns {Boolean} | ||
*/ | ||
}, { | ||
key: 'has', | ||
value: function has(id) { | ||
return Boolean(this._map[id]); | ||
} | ||
/** | ||
* Resolves pending promise by `id` with specified `value`. | ||
* Throws if promise does not exist. | ||
* | ||
* @param {String} id | ||
* @param {*} [value] | ||
*/ | ||
}, { | ||
key: 'resolve', | ||
value: function resolve(id, value) { | ||
var pending = this._get(id, { throws: true }); | ||
pending.resolve(value); | ||
} | ||
/** | ||
* Rejects pending promise by `id` with specified `reason`. | ||
* Throws if promise does not exist. | ||
* | ||
* @param {String} id | ||
* @param {*} [reason] | ||
*/ | ||
}, { | ||
key: 'reject', | ||
value: function reject(id, reason) { | ||
var pending = this._get(id, { throws: true }); | ||
pending.reject(reason); | ||
} | ||
/** | ||
* Rejects pending promise by `id` if `reason` is truthy, otherwise resolves with `value`. | ||
* Throws if promise does not exist. | ||
* | ||
* @param {String} id | ||
* @param {*} [value] | ||
* @param {*} [reason] | ||
*/ | ||
}, { | ||
key: 'fulfill', | ||
value: function fulfill(id, value, reason) { | ||
var pending = this._get(id, { throws: true }); | ||
pending.fulfill(value, reason); | ||
} | ||
/** | ||
* Resolves pending promise by `id` with specified `value` if it exists. | ||
* | ||
* @param {String} id | ||
* @param {*} [value] | ||
*/ | ||
}, { | ||
key: 'tryResolve', | ||
value: function tryResolve(id, value) { | ||
var pending = this._get(id, { throws: false }); | ||
if (pending) { | ||
pending.resolve(value); | ||
} | ||
} | ||
/** | ||
* Rejects pending promise by `id` with specified `reason` if it exists. | ||
* | ||
* @param {String} id | ||
* @param {*} [reason] | ||
*/ | ||
}, { | ||
key: 'tryReject', | ||
value: function tryReject(id, reason) { | ||
var pending = this._get(id, { throws: false }); | ||
if (pending) { | ||
pending.reject(reason); | ||
} | ||
} | ||
/** | ||
* Rejects pending promise by `id` if `reason` is truthy, otherwise resolves with `value`. | ||
* | ||
* @param {String} id | ||
* @param {*} [value] | ||
* @param {*} [reason] | ||
*/ | ||
}, { | ||
key: 'tryFulfill', | ||
value: function tryFulfill(id, value, reason) { | ||
var pending = this._get(id, { throws: false }); | ||
if (pending) { | ||
pending.fulfill(value, reason); | ||
} | ||
} | ||
/** | ||
* Rejects all pending promises with specified `reason`. Useful for cleanup. | ||
* | ||
* @param {*} [reason] | ||
*/ | ||
}, { | ||
key: 'rejectAll', | ||
value: function rejectAll(reason) { | ||
var _this2 = this; | ||
Object.keys(this._map).forEach(function (id) { | ||
return _this2.reject(id, reason); | ||
}); | ||
} | ||
/** | ||
* Returns promise of pending object with specified `id`. | ||
* | ||
* @param {String} id | ||
* @returns {Promise|undefined} | ||
*/ | ||
}, { | ||
key: 'getPromise', | ||
value: function getPromise(id) { | ||
return this._map[id] && this._map[id].finalPromise; | ||
} | ||
/** | ||
* Generates unique ID. Can be overwritten. | ||
* | ||
* @returns {String} | ||
*/ | ||
}, { | ||
key: 'generateId', | ||
value: function generateId() { | ||
return '' + this._idPrefix + Date.now() + '-' + Math.random(); | ||
} | ||
}, { | ||
key: '_get', | ||
value: function _get(id, _ref) { | ||
var throws = _ref.throws; | ||
var pending = this._map[id]; | ||
if (!pending && throws) { | ||
throw new Error('Pending promise not found with id: ' + id); | ||
} else { | ||
return pending; | ||
} | ||
} | ||
}, { | ||
key: '_getTimeout', | ||
value: function _getTimeout(options) { | ||
options = options || {}; | ||
return options.timeout !== undefined ? options.timeout : this._timeout; | ||
} | ||
}]); | ||
return Pendings; | ||
}(); | ||
module.exports = Pendings; | ||
module.exports.Pending = Pending; | ||
module.exports.Pending = Pending; | ||
module.exports.TimeoutError = TimeoutError; |
@@ -11,7 +11,7 @@ /** | ||
var promiseFinally = require('promise.prototype.finally'); | ||
var TimeoutError = require('./timeout-error'); | ||
var Pending = function () { | ||
/** | ||
* Constructor. | ||
* Creates instance of single pending promise. It holds `resolve / reject` callbacks for future fulfillment. | ||
*/ | ||
@@ -23,4 +23,7 @@ function Pending() { | ||
this._reject = null; | ||
this._isFulfilled = true; | ||
this._isResolved = true; | ||
this._isRejected = false; | ||
this._promise = null; | ||
this._timer = null; | ||
this._onFulfilled = function () {}; | ||
} | ||
@@ -41,2 +44,3 @@ | ||
* Calls `fn`, returns new promise and holds `resolve` / `reject` callbacks. | ||
* If `timeout` specified, the promise will be rejected after `timeout` with `PendingTimeoutError`. | ||
* | ||
@@ -48,8 +52,6 @@ * @param {Function} fn | ||
value: function call(fn, timeout) { | ||
this._isFulfilled = false; | ||
this._createPromise(fn); | ||
if (timeout) { | ||
this._wrapWithTimeout(timeout); | ||
if (this.isFulfilled) { | ||
this._reset(); | ||
this._createPromise(fn, timeout); | ||
} | ||
this._wrapWithFinally(); | ||
return this._promise; | ||
@@ -67,3 +69,8 @@ } | ||
value: function resolve(value) { | ||
this._resolve(value); | ||
if (!this.isFulfilled) { | ||
this._isResolved = true; | ||
this._clearTimer(); | ||
this._resolve(value); | ||
this._onFulfilled(this); | ||
} | ||
} | ||
@@ -80,3 +87,8 @@ | ||
value: function reject(reason) { | ||
this._reject(reason); | ||
if (!this.isFulfilled) { | ||
this._isRejected = true; | ||
this._clearTimer(); | ||
this._reject(reason); | ||
this._onFulfilled(this); | ||
} | ||
} | ||
@@ -102,3 +114,11 @@ | ||
key: '_createPromise', | ||
value: function _createPromise(fn) { | ||
value: function _createPromise(fn, timeout) { | ||
this._initPromise(fn); | ||
if (timeout) { | ||
this._initTimer(timeout); | ||
} | ||
} | ||
}, { | ||
key: '_initPromise', | ||
value: function _initPromise(fn) { | ||
var _this = this; | ||
@@ -109,4 +129,4 @@ | ||
_this._reject = reject; | ||
if (fn) { | ||
fn(); | ||
if (typeof fn === 'function') { | ||
_this._callFn(fn); | ||
} | ||
@@ -116,21 +136,34 @@ }); | ||
}, { | ||
key: '_wrapWithTimeout', | ||
value: function _wrapWithTimeout(timeout) { | ||
var timeoutPromise = new Promise(function (resolve, reject) { | ||
return setTimeout(function () { | ||
reject(new Error('Promise rejected by timeout (' + timeout + ' ms)')); | ||
}, timeout); | ||
}); | ||
this._promise = Promise.race([this._promise, timeoutPromise]); | ||
key: '_callFn', | ||
value: function _callFn(fn) { | ||
try { | ||
fn(); | ||
} catch (e) { | ||
this.reject(e); | ||
} | ||
} | ||
}, { | ||
key: '_wrapWithFinally', | ||
value: function _wrapWithFinally() { | ||
key: '_initTimer', | ||
value: function _initTimer(timeout) { | ||
var _this2 = this; | ||
this._promise = promiseFinally(this._promise, function () { | ||
return _this2._isFulfilled = true; | ||
}); | ||
this._timer = setTimeout(function () { | ||
return _this2.reject(new TimeoutError(timeout)); | ||
}, timeout); | ||
} | ||
}, { | ||
key: '_clearTimer', | ||
value: function _clearTimer() { | ||
if (this._timer) { | ||
clearTimeout(this._timer); | ||
this._timer = null; | ||
} | ||
} | ||
}, { | ||
key: '_reset', | ||
value: function _reset() { | ||
this._isResolved = false; | ||
this._isRejected = false; | ||
} | ||
}, { | ||
key: 'promise', | ||
@@ -142,3 +175,3 @@ get: function get() { | ||
/** | ||
* Returns is promise fulfilled or not. | ||
* Returns true if promise resolved. | ||
* | ||
@@ -149,6 +182,46 @@ * @returns {Boolean} | ||
}, { | ||
key: 'isResolved', | ||
get: function get() { | ||
return this._isResolved; | ||
} | ||
/** | ||
* Returns true if promise rejected. | ||
* | ||
* @returns {Boolean} | ||
*/ | ||
}, { | ||
key: 'isRejected', | ||
get: function get() { | ||
return this._isRejected; | ||
} | ||
/** | ||
* Returns true if promise fulfilled (resolved or rejected). | ||
* | ||
* @returns {Boolean} | ||
*/ | ||
}, { | ||
key: 'isFulfilled', | ||
get: function get() { | ||
return this._isFulfilled; | ||
return this._isResolved || this._isRejected; | ||
} | ||
/** | ||
* Callback called when promise is fulfilled (resolved or rejected). | ||
* | ||
* @param {Function} fn | ||
*/ | ||
}, { | ||
key: 'onFulfilled', | ||
set: function set(fn) { | ||
if (typeof fn === 'function') { | ||
this._onFulfilled = fn; | ||
} else { | ||
throw new Error('onFulfilled should be a function.'); | ||
} | ||
} | ||
}]); | ||
@@ -155,0 +228,0 @@ |
{ | ||
"name": "pendings", | ||
"version": "0.1.10", | ||
"version": "0.2.0", | ||
"description": "Better control of pending promises", | ||
@@ -44,9 +44,9 @@ "author": { | ||
"babel-preset-es2015": "^6.24.1", | ||
"chai": "^4.0.2", | ||
"chai-as-promised": "^7.0.0", | ||
"eslint": "^4.0.0", | ||
"husky": "^0.13.4", | ||
"chai": "^4.1.1", | ||
"chai-as-promised": "^7.1.1", | ||
"eslint": "^4.4.1", | ||
"husky": "^0.14.3", | ||
"jsdoc-to-markdown": "^3.0.0", | ||
"lint-staged": "^4.0.0", | ||
"mocha": "^3.4.2" | ||
"lint-staged": "^4.0.3", | ||
"mocha": "^3.5.0" | ||
}, | ||
@@ -63,6 +63,3 @@ "keywords": [ | ||
], | ||
"license": "MIT", | ||
"dependencies": { | ||
"promise.prototype.finally": "^2.0.1" | ||
} | ||
"license": "MIT" | ||
} |
@@ -17,49 +17,42 @@ # Pendings | ||
## Example | ||
When working with events in promise-based code we need to manually store `resolve` / `reject` callbacks | ||
for future fulfillment: | ||
## Usage | ||
When using promises in event-based code we need to manually store `resolve` / `reject` callbacks: | ||
```js | ||
class MyClass { | ||
waitSomeEvent() { | ||
class Foo { | ||
asyncRequest() { | ||
return new Promise((resolve, reject) => { | ||
this.resolve = resolve; | ||
this.reject = reject; | ||
this.request(); | ||
this.send(); | ||
}); | ||
} | ||
onEvent(event) { | ||
this.resolve(event.data); | ||
} | ||
``` | ||
and resolve later: | ||
```js | ||
onSuccess(data) { | ||
this.resolve(data); | ||
} | ||
onError(event) { | ||
this.reject(event.data); | ||
} | ||
} | ||
``` | ||
*Pendings* allows to reduce boilerplate code: | ||
``` | ||
*Pendings* allows to do it simpler: | ||
```js | ||
const Pending = require('pendings').Pending; | ||
class MyClass { | ||
class Foo { | ||
constructor() { | ||
this.pending = new Pending(); | ||
} | ||
waitSomeEvent() { | ||
return this.pending.call(() => this.request()); | ||
} | ||
onEvent(event) { | ||
this.pending.resolve(event.data); | ||
asyncRequest() { | ||
return this.pending.call(() => this.send()); | ||
} | ||
onError(event) { | ||
this.pending.reject(event.data); | ||
} | ||
} | ||
``` | ||
This is even more useful for list of pending promises. | ||
and resolve later: | ||
```js | ||
onSuccess(data) { | ||
this.pending.resolve(data); | ||
} | ||
``` | ||
This is even more useful for list of promises. | ||
Each promise automatically gets unique `id` that allows to fulfill it later: | ||
@@ -69,3 +62,3 @@ ```js | ||
class MyClass { | ||
class Foo { | ||
constructor() { | ||
@@ -75,17 +68,14 @@ this.pendings = new Pendings(); | ||
sendDataAndWaitResponse(data) { | ||
return this.pendings.add(id => { // `id` is generated by Pendings | ||
data.id = id; | ||
this.send(data); | ||
asyncRequest() { | ||
return this.pendings.add(id => { | ||
this.send({id, foo: 'bar'}); // mark request with unique `id` generated by Pendings | ||
}); | ||
} | ||
onEvent(event) { | ||
const data = event.data; | ||
this.pendings.resolve(data.id, data); // response event should contain `id` property | ||
onSuccess(data) { | ||
this.pendings.resolve(data.id, data); // resolve by `id` property of event | ||
} | ||
onError(event) { | ||
const data = event.data; | ||
this.pendings.reject(data.id, data); | ||
onError(data) { | ||
this.pendings.reject(data.id, data); // reject by `id` property of event | ||
} | ||
@@ -92,0 +82,0 @@ } |
280
README.md
@@ -17,49 +17,42 @@ # Pendings | ||
## Example | ||
When working with events in promise-based code we need to manually store `resolve` / `reject` callbacks | ||
for future fulfillment: | ||
## Usage | ||
When using promises in event-based code we need to manually store `resolve` / `reject` callbacks: | ||
```js | ||
class MyClass { | ||
waitSomeEvent() { | ||
class Foo { | ||
asyncRequest() { | ||
return new Promise((resolve, reject) => { | ||
this.resolve = resolve; | ||
this.reject = reject; | ||
this.request(); | ||
this.send(); | ||
}); | ||
} | ||
onEvent(event) { | ||
this.resolve(event.data); | ||
} | ||
``` | ||
and resolve later: | ||
```js | ||
onSuccess(data) { | ||
this.resolve(data); | ||
} | ||
onError(event) { | ||
this.reject(event.data); | ||
} | ||
} | ||
``` | ||
*Pendings* allows to reduce boilerplate code: | ||
``` | ||
*Pendings* allows to do it simpler: | ||
```js | ||
const Pending = require('pendings').Pending; | ||
class MyClass { | ||
class Foo { | ||
constructor() { | ||
this.pending = new Pending(); | ||
} | ||
waitSomeEvent() { | ||
return this.pending.call(() => this.request()); | ||
} | ||
onEvent(event) { | ||
this.pending.resolve(event.data); | ||
asyncRequest() { | ||
return this.pending.call(() => this.send()); | ||
} | ||
onError(event) { | ||
this.pending.reject(event.data); | ||
} | ||
} | ||
``` | ||
This is even more useful for list of pending promises. | ||
and resolve later: | ||
```js | ||
onSuccess(data) { | ||
this.pending.resolve(data); | ||
} | ||
``` | ||
This is even more useful for list of promises. | ||
Each promise automatically gets unique `id` that allows to fulfill it later: | ||
@@ -69,3 +62,3 @@ ```js | ||
class MyClass { | ||
class Foo { | ||
constructor() { | ||
@@ -75,17 +68,14 @@ this.pendings = new Pendings(); | ||
sendDataAndWaitResponse(data) { | ||
return this.pendings.add(id => { // `id` is generated by Pendings | ||
data.id = id; | ||
this.send(data); | ||
asyncRequest() { | ||
return this.pendings.add(id => { | ||
this.send({id, foo: 'bar'}); // mark request with unique `id` generated by Pendings | ||
}); | ||
} | ||
onEvent(event) { | ||
const data = event.data; | ||
this.pendings.resolve(data.id, data); // response event should contain `id` property | ||
onSuccess(data) { | ||
this.pendings.resolve(data.id, data); // resolve by `id` property of event | ||
} | ||
onError(event) { | ||
const data = event.data; | ||
this.pendings.reject(data.id, data); | ||
onError(data) { | ||
this.pendings.reject(data.id, data); // reject by `id` property of event | ||
} | ||
@@ -100,8 +90,114 @@ } | ||
<dl> | ||
<dt><a href="#Pending">Pending</a></dt> | ||
<dd></dd> | ||
<dt><a href="#Pendings">Pendings</a></dt> | ||
<dd></dd> | ||
<dt><a href="#Pending">Pending</a></dt> | ||
<dt><a href="#TimeoutError">TimeoutError</a></dt> | ||
<dd></dd> | ||
</dl> | ||
<a name="Pending"></a> | ||
## Pending | ||
**Kind**: global class | ||
* [Pending](#Pending) | ||
* [new Pending()](#new_Pending_new) | ||
* [.promise](#Pending+promise) ⇒ <code>Promise</code> | ||
* [.isResolved](#Pending+isResolved) ⇒ <code>Boolean</code> | ||
* [.isRejected](#Pending+isRejected) ⇒ <code>Boolean</code> | ||
* [.isFulfilled](#Pending+isFulfilled) ⇒ <code>Boolean</code> | ||
* [.onFulfilled](#Pending+onFulfilled) | ||
* [.call(fn, [timeout])](#Pending+call) ⇒ <code>Promise</code> | ||
* [.resolve([value])](#Pending+resolve) | ||
* [.reject([reason])](#Pending+reject) | ||
* [.fulfill([value], [reason])](#Pending+fulfill) | ||
<a name="new_Pending_new"></a> | ||
### new Pending() | ||
Creates instance of single pending promise. It holds `resolve / reject` callbacks for future fulfillment. | ||
<a name="Pending+promise"></a> | ||
### pending.promise ⇒ <code>Promise</code> | ||
Returns promise itself. | ||
**Kind**: instance property of [<code>Pending</code>](#Pending) | ||
<a name="Pending+isResolved"></a> | ||
### pending.isResolved ⇒ <code>Boolean</code> | ||
Returns true if promise resolved. | ||
**Kind**: instance property of [<code>Pending</code>](#Pending) | ||
<a name="Pending+isRejected"></a> | ||
### pending.isRejected ⇒ <code>Boolean</code> | ||
Returns true if promise rejected. | ||
**Kind**: instance property of [<code>Pending</code>](#Pending) | ||
<a name="Pending+isFulfilled"></a> | ||
### pending.isFulfilled ⇒ <code>Boolean</code> | ||
Returns true if promise fulfilled (resolved or rejected). | ||
**Kind**: instance property of [<code>Pending</code>](#Pending) | ||
<a name="Pending+onFulfilled"></a> | ||
### pending.onFulfilled | ||
Callback called when promise is fulfilled (resolved or rejected). | ||
**Kind**: instance property of [<code>Pending</code>](#Pending) | ||
| Param | Type | | ||
| --- | --- | | ||
| fn | <code>function</code> | | ||
<a name="Pending+call"></a> | ||
### pending.call(fn, [timeout]) ⇒ <code>Promise</code> | ||
Calls `fn`, returns new promise and holds `resolve` / `reject` callbacks. | ||
If `timeout` specified, the promise will be rejected after `timeout` with `PendingTimeoutError`. | ||
**Kind**: instance method of [<code>Pending</code>](#Pending) | ||
| Param | Type | Default | | ||
| --- | --- | --- | | ||
| fn | <code>function</code> | | | ||
| [timeout] | <code>Number</code> | <code>0</code> | | ||
<a name="Pending+resolve"></a> | ||
### pending.resolve([value]) | ||
Resolves pending promise with specified `value`. | ||
**Kind**: instance method of [<code>Pending</code>](#Pending) | ||
| Param | Type | | ||
| --- | --- | | ||
| [value] | <code>\*</code> | | ||
<a name="Pending+reject"></a> | ||
### pending.reject([reason]) | ||
Rejects pending promise with specified `reason`. | ||
**Kind**: instance method of [<code>Pending</code>](#Pending) | ||
| Param | Type | | ||
| --- | --- | | ||
| [reason] | <code>\*</code> | | ||
<a name="Pending+fulfill"></a> | ||
### pending.fulfill([value], [reason]) | ||
Rejects if `reason` is truthy, otherwise resolves with `value`. | ||
**Kind**: instance method of [<code>Pending</code>](#Pending) | ||
| Param | Type | | ||
| --- | --- | | ||
| [value] | <code>\*</code> | | ||
| [reason] | <code>\*</code> | | ||
<a name="Pendings"></a> | ||
@@ -124,3 +220,2 @@ | ||
* [.rejectAll([reason])](#Pendings+rejectAll) | ||
* [.getPromise(id)](#Pendings+getPromise) ⇒ <code>Promise</code> \| <code>undefined</code> | ||
* [.generateId()](#Pendings+generateId) ⇒ <code>String</code> | ||
@@ -131,3 +226,3 @@ | ||
### new Pendings([options]) | ||
Constructor. | ||
Creates list of pending promises. | ||
@@ -158,2 +253,3 @@ | ||
Calls `fn` and returns new promise with specified `id`. | ||
If promise with such `id` already pending - it will be returned. | ||
@@ -172,3 +268,3 @@ **Kind**: instance method of [<code>Pendings</code>](#Pendings) | ||
### pendings.has(id) ⇒ <code>Boolean</code> | ||
Checks if pending promise with specified `id` exists. | ||
Checks if promise with specified `id` is pending. | ||
@@ -185,3 +281,3 @@ **Kind**: instance method of [<code>Pendings</code>](#Pendings) | ||
Resolves pending promise by `id` with specified `value`. | ||
Throws if promise does not exist. | ||
Throws if promise does not exist or is already fulfilled. | ||
@@ -199,3 +295,3 @@ **Kind**: instance method of [<code>Pendings</code>](#Pendings) | ||
Rejects pending promise by `id` with specified `reason`. | ||
Throws if promise does not exist. | ||
Throws if promise does not exist or is already fulfilled. | ||
@@ -213,3 +309,3 @@ **Kind**: instance method of [<code>Pendings</code>](#Pendings) | ||
Rejects pending promise by `id` if `reason` is truthy, otherwise resolves with `value`. | ||
Throws if promise does not exist. | ||
Throws if promise does not exist or is already fulfilled. | ||
@@ -272,13 +368,2 @@ **Kind**: instance method of [<code>Pendings</code>](#Pendings) | ||
<a name="Pendings+getPromise"></a> | ||
### pendings.getPromise(id) ⇒ <code>Promise</code> \| <code>undefined</code> | ||
Returns promise of pending object with specified `id`. | ||
**Kind**: instance method of [<code>Pendings</code>](#Pendings) | ||
| Param | Type | | ||
| --- | --- | | ||
| id | <code>String</code> | | ||
<a name="Pendings+generateId"></a> | ||
@@ -290,81 +375,18 @@ | ||
**Kind**: instance method of [<code>Pendings</code>](#Pendings) | ||
<a name="Pending"></a> | ||
<a name="TimeoutError"></a> | ||
## Pending | ||
## TimeoutError | ||
**Kind**: global class | ||
<a name="new_TimeoutError_new"></a> | ||
* [Pending](#Pending) | ||
* [new Pending()](#new_Pending_new) | ||
* [.promise](#Pending+promise) ⇒ <code>Promise</code> | ||
* [.isFulfilled](#Pending+isFulfilled) ⇒ <code>Boolean</code> | ||
* [.call(fn, [timeout])](#Pending+call) ⇒ <code>Promise</code> | ||
* [.resolve([value])](#Pending+resolve) | ||
* [.reject([reason])](#Pending+reject) | ||
* [.fulfill([value], [reason])](#Pending+fulfill) | ||
### new TimeoutError(timeout) | ||
Timeout error for pending promise. | ||
<a name="new_Pending_new"></a> | ||
### new Pending() | ||
Constructor. | ||
<a name="Pending+promise"></a> | ||
### pending.promise ⇒ <code>Promise</code> | ||
Returns promise itself. | ||
**Kind**: instance property of [<code>Pending</code>](#Pending) | ||
<a name="Pending+isFulfilled"></a> | ||
### pending.isFulfilled ⇒ <code>Boolean</code> | ||
Returns is promise fulfilled or not. | ||
**Kind**: instance property of [<code>Pending</code>](#Pending) | ||
<a name="Pending+call"></a> | ||
### pending.call(fn, [timeout]) ⇒ <code>Promise</code> | ||
Calls `fn`, returns new promise and holds `resolve` / `reject` callbacks. | ||
**Kind**: instance method of [<code>Pending</code>](#Pending) | ||
| Param | Type | Default | | ||
| --- | --- | --- | | ||
| fn | <code>function</code> | | | ||
| [timeout] | <code>Number</code> | <code>0</code> | | ||
<a name="Pending+resolve"></a> | ||
### pending.resolve([value]) | ||
Resolves pending promise with specified `value`. | ||
**Kind**: instance method of [<code>Pending</code>](#Pending) | ||
| Param | Type | | ||
| --- | --- | | ||
| [value] | <code>\*</code> | | ||
| timeout | <code>Number</code> | | ||
<a name="Pending+reject"></a> | ||
### pending.reject([reason]) | ||
Rejects pending promise with specified `reason`. | ||
**Kind**: instance method of [<code>Pending</code>](#Pending) | ||
| Param | Type | | ||
| --- | --- | | ||
| [reason] | <code>\*</code> | | ||
<a name="Pending+fulfill"></a> | ||
### pending.fulfill([value], [reason]) | ||
Rejects if `reason` is truthy, otherwise resolves with `value`. | ||
**Kind**: instance method of [<code>Pending</code>](#Pending) | ||
| Param | Type | | ||
| --- | --- | | ||
| [value] | <code>\*</code> | | ||
| [reason] | <code>\*</code> | | ||
## License | ||
MIT @ [Vitaliy Potapov](https://github.com/vitalets) |
185
src/index.js
@@ -1,188 +0,9 @@ | ||
/** | ||
* Controls list of pending promises. | ||
*/ | ||
'use strict'; | ||
const promiseFinally = require('promise.prototype.finally'); | ||
const Pendings = require('./pendings'); | ||
const Pending = require('./pending'); | ||
const TimeoutError = require('./timeout-error'); | ||
class Pendings { | ||
/** | ||
* Constructor. | ||
* | ||
* @param {Object} [options] | ||
* @param {String} [options.idPrefix=''] prefix for generated IDs | ||
* @param {Number} [options.timeout=0] default timeout for all promises | ||
*/ | ||
constructor(options) { | ||
options = options || {}; | ||
this._timeout = options.timeout || 0; | ||
this._idPrefix = options.idPrefix || ''; | ||
this._map = Object.create(null); | ||
} | ||
/** | ||
* Calls `fn` and returns new promise. `fn` gets generated unique `id` as parameter. | ||
* | ||
* @param {Function} fn | ||
* @param {Object} [options] | ||
* @param {Number} [options.timeout] custom timeout for particular promise | ||
* @returns {Promise} | ||
*/ | ||
add(fn, options) { | ||
const id = this.generateId(); | ||
return this.set(id, fn, options); | ||
} | ||
/** | ||
* Calls `fn` and returns new promise with specified `id`. | ||
* | ||
* @param {String} id | ||
* @param {Function} fn | ||
* @param {Object} [options] | ||
* @param {Number} [options.timeout] custom timeout for particular promise | ||
* @returns {Promise} | ||
*/ | ||
set(id, fn, options) { | ||
if (!this.has(id)) { | ||
const pending = this._map[id] = new Pending(); | ||
const timeout = this._getTimeout(options); | ||
const promise = pending.call(() => fn(id), timeout); | ||
pending.finalPromise = promiseFinally(promise, () => delete this._map[id]); | ||
} | ||
return this.getPromise(id); | ||
} | ||
/** | ||
* Checks if pending promise with specified `id` exists. | ||
* | ||
* @param {String} id | ||
* @returns {Boolean} | ||
*/ | ||
has(id) { | ||
return Boolean(this._map[id]); | ||
} | ||
/** | ||
* Resolves pending promise by `id` with specified `value`. | ||
* Throws if promise does not exist. | ||
* | ||
* @param {String} id | ||
* @param {*} [value] | ||
*/ | ||
resolve(id, value) { | ||
const pending = this._get(id, {throws: true}); | ||
pending.resolve(value); | ||
} | ||
/** | ||
* Rejects pending promise by `id` with specified `reason`. | ||
* Throws if promise does not exist. | ||
* | ||
* @param {String} id | ||
* @param {*} [reason] | ||
*/ | ||
reject(id, reason) { | ||
const pending = this._get(id, {throws: true}); | ||
pending.reject(reason); | ||
} | ||
/** | ||
* Rejects pending promise by `id` if `reason` is truthy, otherwise resolves with `value`. | ||
* Throws if promise does not exist. | ||
* | ||
* @param {String} id | ||
* @param {*} [value] | ||
* @param {*} [reason] | ||
*/ | ||
fulfill(id, value, reason) { | ||
const pending = this._get(id, {throws: true}); | ||
pending.fulfill(value, reason); | ||
} | ||
/** | ||
* Resolves pending promise by `id` with specified `value` if it exists. | ||
* | ||
* @param {String} id | ||
* @param {*} [value] | ||
*/ | ||
tryResolve(id, value) { | ||
const pending = this._get(id, {throws: false}); | ||
if (pending) { | ||
pending.resolve(value); | ||
} | ||
} | ||
/** | ||
* Rejects pending promise by `id` with specified `reason` if it exists. | ||
* | ||
* @param {String} id | ||
* @param {*} [reason] | ||
*/ | ||
tryReject(id, reason) { | ||
const pending = this._get(id, {throws: false}); | ||
if (pending) { | ||
pending.reject(reason); | ||
} | ||
} | ||
/** | ||
* Rejects pending promise by `id` if `reason` is truthy, otherwise resolves with `value`. | ||
* | ||
* @param {String} id | ||
* @param {*} [value] | ||
* @param {*} [reason] | ||
*/ | ||
tryFulfill(id, value, reason) { | ||
const pending = this._get(id, {throws: false}); | ||
if (pending) { | ||
pending.fulfill(value, reason); | ||
} | ||
} | ||
/** | ||
* Rejects all pending promises with specified `reason`. Useful for cleanup. | ||
* | ||
* @param {*} [reason] | ||
*/ | ||
rejectAll(reason) { | ||
Object.keys(this._map).forEach(id => this.reject(id, reason)); | ||
} | ||
/** | ||
* Returns promise of pending object with specified `id`. | ||
* | ||
* @param {String} id | ||
* @returns {Promise|undefined} | ||
*/ | ||
getPromise(id) { | ||
return this._map[id] && this._map[id].finalPromise; | ||
} | ||
/** | ||
* Generates unique ID. Can be overwritten. | ||
* | ||
* @returns {String} | ||
*/ | ||
generateId() { | ||
return `${this._idPrefix}${Date.now()}-${Math.random()}`; | ||
} | ||
_get(id, {throws}) { | ||
const pending = this._map[id]; | ||
if (!pending && throws) { | ||
throw new Error(`Pending promise not found with id: ${id}`); | ||
} else { | ||
return pending; | ||
} | ||
} | ||
_getTimeout(options) { | ||
options = options || {}; | ||
return options.timeout !== undefined ? options.timeout : this._timeout; | ||
} | ||
} | ||
module.exports = Pendings; | ||
module.exports.Pending = Pending; | ||
module.exports.TimeoutError = TimeoutError; |
@@ -7,7 +7,7 @@ /** | ||
const promiseFinally = require('promise.prototype.finally'); | ||
const TimeoutError = require('./timeout-error'); | ||
class Pending { | ||
/** | ||
* Constructor. | ||
* Creates instance of single pending promise. It holds `resolve / reject` callbacks for future fulfillment. | ||
*/ | ||
@@ -17,4 +17,7 @@ constructor() { | ||
this._reject = null; | ||
this._isFulfilled = true; | ||
this._isResolved = true; | ||
this._isRejected = false; | ||
this._promise = null; | ||
this._timer = null; | ||
this._onFulfilled = () => {}; | ||
} | ||
@@ -32,12 +35,44 @@ | ||
/** | ||
* Returns is promise fulfilled or not. | ||
* Returns true if promise resolved. | ||
* | ||
* @returns {Boolean} | ||
*/ | ||
get isResolved() { | ||
return this._isResolved; | ||
} | ||
/** | ||
* Returns true if promise rejected. | ||
* | ||
* @returns {Boolean} | ||
*/ | ||
get isRejected() { | ||
return this._isRejected; | ||
} | ||
/** | ||
* Returns true if promise fulfilled (resolved or rejected). | ||
* | ||
* @returns {Boolean} | ||
*/ | ||
get isFulfilled() { | ||
return this._isFulfilled; | ||
return this._isResolved || this._isRejected; | ||
} | ||
/** | ||
* Callback called when promise is fulfilled (resolved or rejected). | ||
* | ||
* @param {Function} fn | ||
*/ | ||
set onFulfilled(fn) { | ||
if (typeof fn === 'function') { | ||
this._onFulfilled = fn; | ||
} else { | ||
throw new Error('onFulfilled should be a function.'); | ||
} | ||
} | ||
/** | ||
* Calls `fn`, returns new promise and holds `resolve` / `reject` callbacks. | ||
* If `timeout` specified, the promise will be rejected after `timeout` with `PendingTimeoutError`. | ||
* | ||
@@ -49,8 +84,6 @@ * @param {Function} fn | ||
call(fn, timeout) { | ||
this._isFulfilled = false; | ||
this._createPromise(fn); | ||
if (timeout) { | ||
this._wrapWithTimeout(timeout); | ||
if (this.isFulfilled) { | ||
this._reset(); | ||
this._createPromise(fn, timeout); | ||
} | ||
this._wrapWithFinally(); | ||
return this._promise; | ||
@@ -65,3 +98,8 @@ } | ||
resolve(value) { | ||
this._resolve(value); | ||
if (!this.isFulfilled) { | ||
this._isResolved = true; | ||
this._clearTimer(); | ||
this._resolve(value); | ||
this._onFulfilled(this); | ||
} | ||
} | ||
@@ -75,3 +113,8 @@ | ||
reject(reason) { | ||
this._reject(reason); | ||
if (!this.isFulfilled) { | ||
this._isRejected = true; | ||
this._clearTimer(); | ||
this._reject(reason); | ||
this._onFulfilled(this); | ||
} | ||
} | ||
@@ -93,8 +136,15 @@ | ||
_createPromise(fn) { | ||
_createPromise(fn, timeout) { | ||
this._initPromise(fn); | ||
if (timeout) { | ||
this._initTimer(timeout); | ||
} | ||
} | ||
_initPromise(fn) { | ||
this._promise = new Promise((resolve, reject) => { | ||
this._resolve = resolve; | ||
this._reject = reject; | ||
if (fn) { | ||
fn(); | ||
if (typeof fn === 'function') { | ||
this._callFn(fn); | ||
} | ||
@@ -104,14 +154,27 @@ }); | ||
_wrapWithTimeout(timeout) { | ||
const timeoutPromise = new Promise((resolve, reject) => setTimeout(() => { | ||
reject(new Error(`Promise rejected by timeout (${timeout} ms)`)); | ||
}, timeout)); | ||
this._promise = Promise.race([this._promise, timeoutPromise]); | ||
_callFn(fn) { | ||
try { | ||
fn(); | ||
} catch (e) { | ||
this.reject(e); | ||
} | ||
} | ||
_wrapWithFinally() { | ||
this._promise = promiseFinally(this._promise, () => this._isFulfilled = true); | ||
_initTimer(timeout) { | ||
this._timer = setTimeout(() => this.reject(new TimeoutError(timeout)), timeout); | ||
} | ||
_clearTimer() { | ||
if (this._timer) { | ||
clearTimeout(this._timer); | ||
this._timer = null; | ||
} | ||
} | ||
_reset() { | ||
this._isResolved = false; | ||
this._isRejected = false; | ||
} | ||
} | ||
module.exports = Pending; |
@@ -9,2 +9,6 @@ 'use strict'; | ||
global.assert = chai.assert; | ||
global.expect = chai.expect; | ||
global.srcPath = process.env.TEST_LIB ? '../../lib' : '../../src'; | ||
// see: https://github.com/domenic/chai-as-promised/issues/173 | ||
process.on('unhandledRejection', () => {}); |
@@ -5,2 +5,4 @@ 'use strict'; | ||
const noop = () => {}; | ||
describe('pending', function () { | ||
@@ -11,96 +13,164 @@ beforeEach(function () { | ||
it('should return Promise', function () { | ||
const res = this.pending.call(() => {}); | ||
assert.instanceOf(res, Promise); | ||
}); | ||
describe('call', function () { | ||
it('should return Promise', function () { | ||
const res = this.pending.call(noop); | ||
assert.instanceOf(res, Promise); | ||
}); | ||
it('should store Promise', function () { | ||
const res = this.pending.call(() => {}); | ||
assert.instanceOf(this.pending.promise, Promise); | ||
assert.equal(res, this.pending.promise); | ||
}); | ||
it('should store Promise', function () { | ||
const res = this.pending.call(noop); | ||
assert.instanceOf(this.pending.promise, Promise); | ||
assert.equal(res, this.pending.promise); | ||
}); | ||
it('should call passed fn', function () { | ||
let a = 0; | ||
this.pending.call(() => a++); | ||
assert.equal(a, 1); | ||
}); | ||
it('should call passed fn', function () { | ||
let a = 0; | ||
this.pending.call(() => a++); | ||
assert.equal(a, 1); | ||
}); | ||
it('should allow to call without fn', function () { | ||
const res = this.pending.call(); | ||
this.pending.resolve('foo'); | ||
return assert.eventually.equal(res, 'foo'); | ||
}); | ||
it('should return the same promise for second call if previous was not fulfilled', function () { | ||
const p1 = this.pending.call(noop); | ||
const p2 = this.pending.call(noop); | ||
assert.equal(p1, p2); | ||
}); | ||
it('should reject in case of error in fn', function () { | ||
const res = this.pending.call(() => {throw new Error('err');}); | ||
return assert.isRejected(res, 'err'); | ||
}); | ||
it('should return new promise for second call if previous was fulfilled', function () { | ||
const p1 = this.pending.call(noop); | ||
this.pending.resolve(); | ||
const p2 = this.pending.call(noop); | ||
assert.notEqual(p1, p2); | ||
}); | ||
it('should reject directly', function () { | ||
const res = this.pending.call(() => {}); | ||
this.pending.reject(new Error('err')); | ||
return assert.isRejected(res, 'err'); | ||
it('should allow to call without fn', function () { | ||
const res = this.pending.call(); | ||
this.pending.resolve('foo'); | ||
return assert.eventually.equal(res, 'foo'); | ||
}); | ||
}); | ||
it('should resolve directly', function () { | ||
const res = this.pending.call(() => {}); | ||
this.pending.resolve('foo'); | ||
return assert.eventually.equal(res, 'foo'); | ||
}); | ||
describe('resolve', function () { | ||
it('should resolve directly', function () { | ||
const res = this.pending.call(noop); | ||
this.pending.resolve('foo'); | ||
return assert.eventually.equal(res, 'foo'); | ||
}); | ||
it('should fulfill to resolved', function () { | ||
const res = this.pending.call(() => {}); | ||
this.pending.fulfill('foo'); | ||
return assert.eventually.equal(res, 'foo'); | ||
it('should keep first value if resolved twice', function () { | ||
const res = this.pending.call(noop); | ||
this.pending.resolve('foo'); | ||
this.pending.resolve('bar'); | ||
return assert.eventually.equal(res, 'foo'); | ||
}); | ||
}); | ||
it('should fulfill to rejected with error', function () { | ||
const res = this.pending.call(() => {}); | ||
this.pending.fulfill('foo', new Error('err')); | ||
return assert.isRejected(res, 'err'); | ||
}); | ||
describe('reject', function () { | ||
it('should reject in case of error in fn', function () { | ||
const res = this.pending.call(() => { | ||
throw new Error('err'); | ||
}); | ||
return assert.isRejected(res, 'err'); | ||
}); | ||
it('should not throw if resolved twice', function () { | ||
const res = this.pending.call(() => {}); | ||
this.pending.resolve('foo'); | ||
this.pending.resolve('bar'); | ||
return assert.eventually.equal(res, 'foo'); | ||
}); | ||
it('should reject directly', function () { | ||
const res = this.pending.call(noop); | ||
this.pending.reject(new Error('err')); | ||
return assert.isRejected(res, 'err'); | ||
}); | ||
it('should set isFulfilled after resolve', function () { | ||
assert.ok(this.pending.isFulfilled); | ||
const res = this.pending.call(); | ||
assert.notOk(this.pending.isFulfilled); | ||
this.pending.resolve('foo'); | ||
return assert.isFulfilled(res).then(() => assert.ok(this.pending.isFulfilled)); | ||
it('should keep first value if rejected twice', function () { | ||
const res = this.pending.call(noop); | ||
this.pending.reject(new Error('foo')); | ||
this.pending.reject(new Error('bar')); | ||
return assert.isRejected(res, 'foo'); | ||
}); | ||
}); | ||
it('should set isFulfilled after reject', function () { | ||
assert.ok(this.pending.isFulfilled); | ||
const res = this.pending.call(); | ||
assert.notOk(this.pending.isFulfilled); | ||
this.pending.reject('foo'); | ||
return assert.isRejected(res, 'foo'); | ||
describe('fulfill', function () { | ||
it('should resolve', function () { | ||
const res = this.pending.call(noop); | ||
this.pending.fulfill('foo'); | ||
return assert.eventually.equal(res, 'foo'); | ||
}); | ||
it('should reject with error', function () { | ||
const res = this.pending.call(noop); | ||
this.pending.fulfill('foo', new Error('err')); | ||
return assert.isRejected(res, 'err'); | ||
}); | ||
it('should keep first value if fulfilled twice', function () { | ||
const res = this.pending.call(noop); | ||
this.pending.fulfill('foo'); | ||
this.pending.fulfill('bar', new Error('err')); | ||
return assert.eventually.equal(res, 'foo'); | ||
}); | ||
}); | ||
it('should set isFulfilled after reject (by error in fn)', function () { | ||
assert.ok(this.pending.isFulfilled); | ||
const res = this.pending.call(() => { throw new Error('err'); }); | ||
assert.notOk(this.pending.isFulfilled); | ||
return assert.isRejected(res, 'err') | ||
.then(() => assert.ok(this.pending.isFulfilled)); | ||
describe('isFulfilled', function () { | ||
it('should set after resolve', function () { | ||
assert.ok(this.pending.isFulfilled); | ||
this.pending.call(); | ||
assert.notOk(this.pending.isFulfilled); | ||
this.pending.resolve('foo'); | ||
assert.ok(this.pending.isFulfilled); | ||
}); | ||
it('should set after reject', function () { | ||
assert.ok(this.pending.isFulfilled); | ||
const res = this.pending.call(); | ||
assert.notOk(this.pending.isFulfilled); | ||
this.pending.reject('foo'); | ||
assert.ok(this.pending.isFulfilled); | ||
return assert.isRejected(res, 'foo'); | ||
}); | ||
it('should set after reject (by error in fn)', function () { | ||
assert.ok(this.pending.isFulfilled); | ||
const res = this.pending.call(() => { | ||
throw new Error('err'); | ||
}); | ||
assert.ok(this.pending.isFulfilled); | ||
return assert.isRejected(res, 'err'); | ||
}); | ||
}); | ||
it('should resolve before timeout', function () { | ||
const res = this.pending.call(() => {}, 10); | ||
setTimeout(() => this.pending.resolve('foo'), 5); | ||
return assert.eventually.equal(res, 'foo'); | ||
describe('onFulfilled', function () { | ||
it('should call after resolve', function () { | ||
let a = 0; | ||
this.pending.onFulfilled = () => a++; | ||
this.pending.call(); | ||
this.pending.resolve('foo'); | ||
assert.equal(a, 1); | ||
}); | ||
it('should set after reject', function () { | ||
let a = 0; | ||
this.pending.onFulfilled = () => a++; | ||
this.pending.call().catch(noop); | ||
this.pending.reject('foo'); | ||
assert.equal(a, 1); | ||
}); | ||
it('should set after reject (by error in fn)', function () { | ||
let a = 0; | ||
this.pending.onFulfilled = () => a++; | ||
this.pending.call(() => { throw new Error('err'); }).catch(noop); | ||
assert.equal(a, 1); | ||
}); | ||
}); | ||
it('should reject after timeout', function () { | ||
const res = this.pending.call(() => {}, 10); | ||
setTimeout(() => this.pending.resolve('foo'), 20); | ||
return assert.isRejected(res, 'Promise rejected by timeout (10 ms)'); | ||
describe('timeout', function () { | ||
it('should resolve before timeout', function () { | ||
const res = this.pending.call(noop, 10); | ||
setTimeout(() => this.pending.resolve('foo'), 5); | ||
return assert.eventually.equal(res, 'foo'); | ||
}); | ||
it('should reject after timeout', function () { | ||
const res = this.pending.call(noop, 10); | ||
setTimeout(() => this.pending.resolve('foo'), 20); | ||
return assert.isRejected(res, 'Promise timeout: 10 ms'); | ||
}); | ||
}); | ||
}); |
@@ -58,2 +58,39 @@ 'use strict'; | ||
describe('has', function () { | ||
it('should return false for non-existing promise', function () { | ||
assert.notOk(this.pendings.has(1)); | ||
}); | ||
it('should return true for pending promise', function () { | ||
this.pendings.set(1, noop); | ||
assert.ok(this.pendings.has(1)); | ||
}); | ||
it('should return false for resolve', function () { | ||
this.pendings.set(1, noop); | ||
this.pendings.resolve(1); | ||
assert.notOk(this.pendings.has(1)); | ||
}); | ||
it('should return false for manual reject', function () { | ||
this.pendings.set(1, noop); | ||
this.pendings.reject(1); | ||
assert.notOk(this.pendings.has(1)); | ||
}); | ||
it('should return false for reject by fn', function () { | ||
this.pendings.set(1, () => { throw new Error('foo'); }); | ||
assert.notOk(this.pendings.has(1)); | ||
}); | ||
it('should return false for reject by timeout', function (done) { | ||
this.pendings.set(1, noop, {timeout: 5}); | ||
assert.ok(this.pendings.has(1)); | ||
setTimeout(() => { | ||
assert.notOk(this.pendings.has(1)); | ||
done(); | ||
}, 10); | ||
}); | ||
}); | ||
describe('resolve', function () { | ||
@@ -70,17 +107,5 @@ it('should resolve', function () { | ||
}); | ||
it('should delete promise after resolve', function () { | ||
const p = this.pendings.set(1, noop); | ||
this.pendings.resolve(1, 1); | ||
return assert.isFulfilled(p) | ||
.then(() => assert.notOk(this.pendings.has(1))); | ||
}); | ||
}); | ||
describe('tryResolve', function () { | ||
it('should not throw for incorrect id', function () { | ||
this.pendings.set(1, noop); | ||
assert.doesNotThrow(() => this.pendings.tryResolve(2, 'foo')); | ||
}); | ||
it('should resolve', function () { | ||
@@ -92,2 +117,7 @@ const res = this.pendings.set(1, noop); | ||
}); | ||
it('should not throw for incorrect id', function () { | ||
this.pendings.set(1, noop); | ||
assert.doesNotThrow(() => this.pendings.tryResolve(2, 'foo')); | ||
}); | ||
}); | ||
@@ -106,9 +136,2 @@ | ||
}); | ||
it('should delete promise after reject', function () { | ||
const p = this.pendings.set(1, noop); | ||
this.pendings.reject(1, 'err'); | ||
return assert.isRejected(p, 'err') | ||
.then(() => assert.notOk(this.pendings.has(1))); | ||
}); | ||
}); | ||
@@ -178,64 +201,19 @@ | ||
it('should reject after timeout', function () { | ||
const res = this.pendings.set(1, noop, {timeout: 10}); | ||
setTimeout(() => this.pendings.tryResolve(1, 'foo'), 20); | ||
return assert.isRejected(res, 'Promise rejected by timeout (10 ms)'); | ||
const res = this.pendings.set(1, noop, {timeout: 5}); | ||
return assert.isRejected(res, 'Promise timeout: 5 ms'); | ||
}); | ||
it('should reject after default timeout', function () { | ||
const pendings = new Pendings({timeout: 10}); | ||
const pendings = new Pendings({timeout: 5}); | ||
const res = pendings.set(1, noop); | ||
setTimeout(() => pendings.tryResolve(1, 'foo'), 20); | ||
return assert.isRejected(res, 'Promise rejected by timeout (10 ms)'); | ||
return assert.isRejected(res, 'Promise timeout: 5 ms'); | ||
}); | ||
it('should overwrite default timeout', function () { | ||
const pendings = new Pendings({timeout: 20}); | ||
const res = pendings.set(1, noop, {timeout: 10}); | ||
setTimeout(() => pendings.tryResolve(1, 'foo'), 15); | ||
return assert.isRejected(res, 'Promise rejected by timeout (10 ms)'); | ||
const pendings = new Pendings({timeout: 10}); | ||
const res = pendings.set(1, noop, {timeout: 5}); | ||
return assert.isRejected(res, 'Promise timeout: 5 ms'); | ||
}); | ||
}); | ||
describe('has', function () { | ||
it('should return false for non-existing promise', function () { | ||
assert.notOk(this.pendings.has(1)); | ||
}); | ||
it('should return true for pending promise', function () { | ||
this.pendings.set(1, noop); | ||
assert.ok(this.pendings.has(1)); | ||
}); | ||
it('should return false for resolved promise', function () { | ||
const p = this.pendings.set(1, noop); | ||
this.pendings.resolve(1); | ||
return assert.isFulfilled(p) | ||
.then(() => assert.notOk(this.pendings.has(1))); | ||
}); | ||
it('should return false for rejected promise', function () { | ||
const p = this.pendings.set(1, noop); | ||
this.pendings.reject(1); | ||
return assert.isRejected(p) | ||
.then(() => assert.notOk(this.pendings.has(1))); | ||
}); | ||
}); | ||
describe('getPromise', function () { | ||
it('should return promise of existing pending', function () { | ||
const p = this.pendings.set(1, noop); | ||
assert.equal(p, this.pendings.getPromise(1)); | ||
}); | ||
it('should return promise of timeouted pending', function () { | ||
const p = this.pendings.set(1, noop, {timeout: 10}); | ||
assert.equal(p, this.pendings.getPromise(1)); | ||
return assert.isRejected(p); | ||
}); | ||
it('should return undefined for non-existing pending', function () { | ||
assert.equal(this.pendings.getPromise(1)); | ||
}); | ||
}); | ||
describe('rejectAll', function () { | ||
@@ -265,5 +243,18 @@ it('should reject all promises', function () { | ||
it('should export Pending as prop', function () { | ||
it('should export Pending', function () { | ||
assert.ok(Pendings.Pending); | ||
}); | ||
it('should export TimeoutError', function () { | ||
assert.ok(Pendings.TimeoutError); | ||
}); | ||
it('should not store fulfilled pendings', function () { | ||
this.pendings.set(1, noop); | ||
this.pendings.set(2, noop).catch(() => {}); | ||
this.pendings.set(3, noop); | ||
this.pendings.resolve(1); | ||
this.pendings.reject(2); | ||
assert.equal(Object.keys(this.pendings._map).length, 1); | ||
}); | ||
}); |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
48681
0
15
1130
380
1
- Removedpromise.prototype.finally@^2.0.1
- Removedarray-buffer-byte-length@1.0.2(transitive)
- Removedarraybuffer.prototype.slice@1.0.4(transitive)
- Removedasync-function@1.0.0(transitive)
- Removedavailable-typed-arrays@1.0.7(transitive)
- Removedcall-bind@1.0.8(transitive)
- Removedcall-bind-apply-helpers@1.0.2(transitive)
- Removedcall-bound@1.0.3(transitive)
- Removeddata-view-buffer@1.0.2(transitive)
- Removeddata-view-byte-length@1.0.2(transitive)
- Removeddata-view-byte-offset@1.0.1(transitive)
- Removeddefine-data-property@1.1.4(transitive)
- Removeddefine-properties@1.2.1(transitive)
- Removeddunder-proto@1.0.1(transitive)
- Removedes-abstract@1.23.9(transitive)
- Removedes-define-property@1.0.1(transitive)
- Removedes-errors@1.3.0(transitive)
- Removedes-object-atoms@1.1.1(transitive)
- Removedes-set-tostringtag@2.1.0(transitive)
- Removedes-to-primitive@1.3.0(transitive)
- Removedfor-each@0.3.5(transitive)
- Removedfunction-bind@1.1.2(transitive)
- Removedfunction.prototype.name@1.1.8(transitive)
- Removedfunctions-have-names@1.2.3(transitive)
- Removedget-intrinsic@1.2.7(transitive)
- Removedget-proto@1.0.1(transitive)
- Removedget-symbol-description@1.1.0(transitive)
- Removedglobalthis@1.0.4(transitive)
- Removedgopd@1.2.0(transitive)
- Removedhas-bigints@1.1.0(transitive)
- Removedhas-property-descriptors@1.0.2(transitive)
- Removedhas-proto@1.2.0(transitive)
- Removedhas-symbols@1.1.0(transitive)
- Removedhas-tostringtag@1.0.2(transitive)
- Removedhasown@2.0.2(transitive)
- Removedinternal-slot@1.1.0(transitive)
- Removedis-array-buffer@3.0.5(transitive)
- Removedis-async-function@2.1.1(transitive)
- Removedis-bigint@1.1.0(transitive)
- Removedis-boolean-object@1.2.2(transitive)
- Removedis-callable@1.2.7(transitive)
- Removedis-data-view@1.0.2(transitive)
- Removedis-date-object@1.1.0(transitive)
- Removedis-finalizationregistry@1.1.1(transitive)
- Removedis-generator-function@1.1.0(transitive)
- Removedis-map@2.0.3(transitive)
- Removedis-number-object@1.1.1(transitive)
- Removedis-regex@1.2.1(transitive)
- Removedis-set@2.0.3(transitive)
- Removedis-shared-array-buffer@1.0.4(transitive)
- Removedis-string@1.1.1(transitive)
- Removedis-symbol@1.1.1(transitive)
- Removedis-typed-array@1.1.15(transitive)
- Removedis-weakmap@2.0.2(transitive)
- Removedis-weakref@1.1.1(transitive)
- Removedis-weakset@2.0.4(transitive)
- Removedisarray@2.0.5(transitive)
- Removedmath-intrinsics@1.1.0(transitive)
- Removedobject-inspect@1.13.4(transitive)
- Removedobject-keys@1.1.1(transitive)
- Removedobject.assign@4.1.7(transitive)
- Removedown-keys@1.0.1(transitive)
- Removedpossible-typed-array-names@1.1.0(transitive)
- Removedpromise.prototype.finally@2.0.1(transitive)
- Removedreflect.getprototypeof@1.0.10(transitive)
- Removedregexp.prototype.flags@1.5.4(transitive)
- Removedsafe-array-concat@1.1.3(transitive)
- Removedsafe-push-apply@1.0.0(transitive)
- Removedsafe-regex-test@1.1.0(transitive)
- Removedset-function-length@1.2.2(transitive)
- Removedset-function-name@2.0.2(transitive)
- Removedset-proto@1.0.0(transitive)
- Removedside-channel@1.1.0(transitive)
- Removedside-channel-list@1.0.0(transitive)
- Removedside-channel-map@1.0.1(transitive)
- Removedside-channel-weakmap@1.0.2(transitive)
- Removedstring.prototype.trim@1.2.10(transitive)
- Removedstring.prototype.trimend@1.0.9(transitive)
- Removedstring.prototype.trimstart@1.0.8(transitive)
- Removedtyped-array-buffer@1.0.3(transitive)
- Removedtyped-array-byte-length@1.0.3(transitive)
- Removedtyped-array-byte-offset@1.0.4(transitive)
- Removedtyped-array-length@1.0.7(transitive)
- Removedunbox-primitive@1.1.0(transitive)
- Removedwhich-boxed-primitive@1.1.1(transitive)
- Removedwhich-builtin-type@1.2.1(transitive)
- Removedwhich-collection@1.0.2(transitive)
- Removedwhich-typed-array@1.1.18(transitive)