New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

pendings

Package Overview
Dependencies
Maintainers
1
Versions
19
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

pendings - npm Package Compare versions

Comparing version 0.1.10 to 0.2.0

lib/pendings.js

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 @@ }

@@ -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)

@@ -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);
});
});
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc