Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

promise-again

Package Overview
Dependencies
Maintainers
1
Versions
6
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

promise-again - npm Package Compare versions

Comparing version 1.2.0 to 1.3.0

6

dist/es6/index.d.ts
export interface IOptions {
delay?: number | ((attempt: number, ...args: any[]) => number);
attempts: number | ((attempt: number, ...args: any[]) => boolean);
retryArgumentsInterceptor?: (reason: any, attempt: number, ...args: any[]) => any[];
delay?: number | ((reason: any, attempt: number, ...args: any[]) => number | Promise<number>);
attempts: number | ((reason: any, attempt: number, ...args: any[]) => boolean | Promise<boolean>);
retryArgumentsInterceptor?: (reason: any, attempt: number, ...args: any[]) => any[] | Promise<any[]>;
}
export default function promiseAgain<T>(func: (...args: any[]) => Promise<T>, options: IOptions): (...args: any[]) => Promise<T>;

@@ -23,3 +23,3 @@ import * as promiseDelay from 'delay';

else {
shouldRetry = options.attempts.apply(options, [usedAttempts].concat(innerArgs));
shouldRetry = options.attempts.apply(options, [reason, usedAttempts].concat(innerArgs));
}

@@ -31,12 +31,15 @@ var nextDelay = 0;

else if (typeof options.delay === 'function') {
nextDelay = options.delay.apply(options, [usedAttempts].concat(innerArgs));
nextDelay = options.delay.apply(options, [reason, usedAttempts].concat(innerArgs));
}
if (shouldRetry) {
return promiseDelay(nextDelay)
.then(function () { return attempt.apply(void 0, newArguments); })
.catch(function (subReason) { return Promise.reject(subReason); });
}
else {
return Promise.reject(reason);
}
return Promise.all([shouldRetry, nextDelay, newArguments]).then(function (_a) {
var resolvedShouldRetry = _a[0], resolvedNextDelay = _a[1], _b = _a[2], resolvedNewArguments = _b === void 0 ? [] : _b;
if (resolvedShouldRetry) {
return promiseDelay(resolvedNextDelay)
.then(function () { return attempt.apply(void 0, resolvedNewArguments); })
.catch(function (subReason) { return Promise.reject(subReason); });
}
else {
return Promise.reject(reason);
}
});
});

@@ -43,0 +46,0 @@ }.apply(void 0, args);

export interface IOptions {
delay?: number | ((attempt: number, ...args: any[]) => number);
attempts: number | ((attempt: number, ...args: any[]) => boolean);
retryArgumentsInterceptor?: (reason: any, attempt: number, ...args: any[]) => any[];
delay?: number | ((reason: any, attempt: number, ...args: any[]) => number | Promise<number>);
attempts: number | ((reason: any, attempt: number, ...args: any[]) => boolean | Promise<boolean>);
retryArgumentsInterceptor?: (reason: any, attempt: number, ...args: any[]) => any[] | Promise<any[]>;
}
export default function promiseAgain<T>(func: (...args: any[]) => Promise<T>, options: IOptions): (...args: any[]) => Promise<T>;

@@ -34,3 +34,3 @@ (function (factory) {

else {
shouldRetry = options.attempts.apply(options, [usedAttempts].concat(innerArgs));
shouldRetry = options.attempts.apply(options, [reason, usedAttempts].concat(innerArgs));
}

@@ -42,12 +42,15 @@ var nextDelay = 0;

else if (typeof options.delay === 'function') {
nextDelay = options.delay.apply(options, [usedAttempts].concat(innerArgs));
nextDelay = options.delay.apply(options, [reason, usedAttempts].concat(innerArgs));
}
if (shouldRetry) {
return promiseDelay(nextDelay)
.then(function () { return attempt.apply(void 0, newArguments); })
.catch(function (subReason) { return Promise.reject(subReason); });
}
else {
return Promise.reject(reason);
}
return Promise.all([shouldRetry, nextDelay, newArguments]).then(function (_a) {
var resolvedShouldRetry = _a[0], resolvedNextDelay = _a[1], _b = _a[2], resolvedNewArguments = _b === void 0 ? [] : _b;
if (resolvedShouldRetry) {
return promiseDelay(resolvedNextDelay)
.then(function () { return attempt.apply(void 0, resolvedNewArguments); })
.catch(function (subReason) { return Promise.reject(subReason); });
}
else {
return Promise.reject(reason);
}
});
});

@@ -54,0 +57,0 @@ }.apply(void 0, args);

@@ -7,3 +7,31 @@ import {expect} from 'chai';

function freePromisesQueue(clock: sinon.SinonFakeTimers, n: number = 10) {
return () => {
let result = promiseDelay(0);
for (let i = 0; i < n; i++) {
result = result.then(() => { const pr = promiseDelay(0); clock.tick(0); return pr; });
}
clock.tick(0);
return result;
};
}
function promiseDelayAndFreeQueue(delay: number, clock: sinon.SinonFakeTimers, n: number = 10): Promise<any> {
return freePromisesQueue(clock, n)().then(promiseDelay(delay)).then(freePromisesQueue(clock, n));
}
describe('promiseAgain', () => {
let clock: sinon.SinonFakeTimers;
beforeEach(() => {
clock = sinon.useFakeTimers();
});
afterEach(() => {
clock.restore();
});
it('should get value using attempts', (done) => {

@@ -21,2 +49,4 @@ const func = sinon.stub();

});
freePromisesQueue(clock)();
});

@@ -38,4 +68,85 @@

});
freePromisesQueue(clock)();
});
it('should use attempts function to decide if retry needed', (done) => {
const func = sinon.stub();
func.onCall(0).rejects('Some reason 1');
func.onCall(1).rejects('Some reason 2');
func.onCall(2).resolves('Needed value');
const attempts = sinon.stub();
attempts.onCall(0).returns(true);
attempts.onCall(1).returns(false);
const composedFunction = promiseAgain(func, {attempts});
composedFunction().then(() => {
throw new Error('Should not succeed');
}).catch((reason) => {
expect(reason.toString()).to.equal('Some reason 2');
done();
});
freePromisesQueue(clock)();
});
it('should use attempts function returning a promise to decide if retry needed', (done) => {
const func = sinon.stub();
func.onCall(0).rejects('Some reason 1');
func.onCall(1).rejects('Some reason 2');
func.onCall(2).resolves('Needed value');
const attempts = sinon.stub();
attempts.onCall(0).resolves(true);
attempts.onCall(1).resolves(false);
const composedFunction = promiseAgain(func, {attempts});
composedFunction().then(() => {
throw new Error('Should not succeed');
}).catch((reason) => {
expect(reason.toString()).to.equal('Some reason 2');
done();
});
freePromisesQueue(clock)();
});
it('should call attempts dunction with reason, sequentially changing args and attempts count', (done) => {
const func = sinon.stub();
func.onCall(0).rejects('Some reason 1');
func.onCall(1).rejects('Some reason 2');
func.onCall(2).resolves('Needed value');
const attempts = sinon.stub();
attempts.onCall(0).returns(true);
attempts.onCall(1).returns(true);
const composedFunction = promiseAgain(func, {attempts});
composedFunction(1, 2, 3, 'some arg').then(() => {
expect(attempts.callCount).to.equal(2);
expect(
attempts.calledWithExactly(
sinon.match({name: 'Some reason 1'}), 1, 1, 2, 3, 'some arg',
),
).to.equal(true);
expect(
attempts.calledWithExactly(
sinon.match({name: 'Some reason 2'}), 2, 1, 2, 3, 'some arg',
),
).to.equal(true);
done();
});
freePromisesQueue(clock)();
});
it('should pass all function arguments to wrapped function', (done) => {

@@ -53,2 +164,4 @@ const func = sinon.stub();

});
freePromisesQueue(clock)();
});

@@ -75,5 +188,7 @@

});
freePromisesQueue(clock)();
});
it('should call retryArgumentsInterceptor with sequentially changing args and attempts count', (done) => {
it('should call the original function with result of retryArgumentsInterceptor returning a promise', (done) => {
const func = sinon.stub();

@@ -86,2 +201,25 @@

const retryArgumentsInterceptor = sinon.stub();
retryArgumentsInterceptor.onCall(0).resolves([5, 6, 7]);
retryArgumentsInterceptor.onCall(1).resolves([8, 9, 10]);
const composedFunction = promiseAgain(func, {attempts: 5, retryArgumentsInterceptor});
composedFunction(1, 2, 3, 'some arg').then(() => {
expect(func.callCount).to.equal(3);
expect(func.calledWithExactly(1, 2, 3, 'some arg')).to.equal(true);
expect(func.calledWithExactly(5, 6, 7)).to.equal(true);
expect(func.calledWithExactly(8, 9, 10)).to.equal(true);
done();
});
freePromisesQueue(clock)();
});
it('should call retryArgumentsInterceptor with reason, sequentially changing args and attempts count', (done) => {
const func = sinon.stub();
func.onCall(0).rejects('Some reason 1');
func.onCall(1).rejects('Some reason 2');
func.onCall(2).resolves('Needed value');
const retryArgumentsInterceptor = sinon.stub();
retryArgumentsInterceptor.onCall(0).returns([5, 6, 7]);

@@ -108,6 +246,7 @@ retryArgumentsInterceptor.onCall(1).returns([8, 9, 10]);

});
freePromisesQueue(clock)();
});
it('should use a delay between retry calls', (done) => {
const clock = sinon.useFakeTimers();
const func = sinon.stub();

@@ -121,3 +260,2 @@

composedFunction(1, 2, 3, 'some arg').then(() => {
clock.restore();
done();

@@ -130,3 +268,3 @@ }).catch(() => {

promiseDelay(50).then(() => {
promiseDelayAndFreeQueue(50, clock).then(() => {
expect(func.callCount).to.equal(1);

@@ -136,3 +274,3 @@ })

promiseDelay(80).then(() => {
promiseDelayAndFreeQueue(80, clock).then(() => {
expect(func.callCount).to.equal(1);

@@ -142,3 +280,3 @@ })

promiseDelay(155).then(() => {
promiseDelayAndFreeQueue(155, clock).then(() => {
expect(func.callCount).to.equal(2);

@@ -148,3 +286,3 @@ })

promiseDelay(260).then(() => {
promiseDelayAndFreeQueue(260, clock).then(() => {
expect(func.callCount).to.equal(3);

@@ -158,3 +296,2 @@ })

it('should use a delay function to get delay if provided', (done) => {
const clock = sinon.useFakeTimers();
const func = sinon.stub();

@@ -174,3 +311,2 @@

composedFunction(1, 2, 3, 'some arg').then(() => {
clock.restore();
done();

@@ -183,3 +319,3 @@ }).catch(() => {

promiseDelay(50).then(() => {
promiseDelayAndFreeQueue(50, clock).then(() => {
expect(func.callCount).to.equal(1);

@@ -189,3 +325,3 @@ })

promiseDelay(80).then(() => {
promiseDelayAndFreeQueue(80, clock).then(() => {
expect(func.callCount).to.equal(1);

@@ -195,3 +331,3 @@ })

promiseDelay(155).then(() => {
promiseDelayAndFreeQueue(155, clock).then(() => {
expect(func.callCount).to.equal(2);

@@ -201,3 +337,3 @@ })

promiseDelay(360).then(() => {
promiseDelayAndFreeQueue(360, clock).then(() => {
expect(func.callCount).to.equal(3);

@@ -210,4 +346,3 @@ })

it('should pass attempts count and sequential changing arguments to delay function', (done) => {
const clock = sinon.useFakeTimers();
it('should use a delay function returning a promise to get delay if provided', (done) => {
const func = sinon.stub();

@@ -219,2 +354,47 @@

const delayFunction = sinon.stub();
delayFunction.onCall(0).resolves(100);
delayFunction.onCall(1).resolves(150);
delayFunction.onCall(2).resolves(250);
const composedFunction = promiseAgain(func, {attempts: 5, delay: delayFunction});
composedFunction(1, 2, 3, 'some arg').then(() => {
done();
}).catch(() => {
throw new Error('Catch');
});
expect(func.callCount).to.equal(1);
promiseDelayAndFreeQueue(50, clock).then(() => {
expect(func.callCount).to.equal(1);
})
.then(() => clock.tick(30));
promiseDelayAndFreeQueue(80, clock).then(() => {
expect(func.callCount).to.equal(1);
})
.then(() => clock.tick(75));
promiseDelayAndFreeQueue(155, clock).then(() => {
expect(func.callCount).to.equal(2);
})
.then(() => clock.tick(205));
promiseDelayAndFreeQueue(360, clock).then(() => {
expect(func.callCount).to.equal(3);
})
.then(() => clock.tick(105));
clock.tick(50);
});
it('should pass reason, attempts count and sequential changing arguments to delay function', (done) => {
const func = sinon.stub();
func.onCall(0).rejects('Some reason 1');
func.onCall(1).rejects('Some reason 2');
func.onCall(2).resolves('Needed value');
const retryArgumentsInterceptor = sinon.stub();

@@ -233,8 +413,11 @@ retryArgumentsInterceptor.onCall(0).returns([5, 6, 7]);

composedFunction(1, 2, 3, 'some arg').then(() => {
clock.restore();
expect(delayFunction.callCount).to.equal(2);
expect(delayFunction.calledWithExactly(1, 1, 2, 3, 'some arg')).to.equal(true);
expect(delayFunction.calledWithExactly(2, 5, 6, 7)).to.equal(true);
expect(
delayFunction.calledWithExactly(sinon.match({name: 'Some reason 1'}), 1, 1, 2, 3, 'some arg'),
).to.equal(true);
expect(
delayFunction.calledWithExactly(sinon.match({name: 'Some reason 2'}), 2, 5, 6, 7),
).to.equal(true);
done();

@@ -247,12 +430,12 @@ }).catch(() => {

promiseDelay(50)
promiseDelayAndFreeQueue(50, clock)
.then(() => clock.tick(30));
promiseDelay(80)
promiseDelayAndFreeQueue(80, clock)
.then(() => clock.tick(75));
promiseDelay(155)
promiseDelayAndFreeQueue(155, clock)
.then(() => clock.tick(105));
promiseDelay(260)
promiseDelayAndFreeQueue(260, clock)
.then(() => clock.tick(105));

@@ -259,0 +442,0 @@

import * as promiseDelay from 'delay';
export interface IOptions {
delay?: number | ((attempt: number, ...args: any[]) => number);
attempts: number | ((attempt: number, ...args: any[]) => boolean);
retryArgumentsInterceptor?: (reason: any, attempt: number, ...args: any[]) => any[];
delay?: number | ((reason: any, attempt: number, ...args: any[]) => number | Promise<number>);
attempts: number | ((reason: any, attempt: number, ...args: any[]) => boolean | Promise<boolean>);
retryArgumentsInterceptor?: (reason: any, attempt: number, ...args: any[]) => any[] | Promise<any[]>;
}

@@ -23,3 +23,3 @@

let shouldRetry = false;
let shouldRetry: boolean | Promise<boolean> = false;

@@ -29,6 +29,6 @@ if (typeof options.attempts === 'number') {

} else {
shouldRetry = options.attempts(usedAttempts, ...innerArgs);
shouldRetry = options.attempts(reason, usedAttempts, ...innerArgs);
}
let nextDelay: number = 0;
let nextDelay: number | Promise<number> = 0;

@@ -38,12 +38,16 @@ if (typeof options.delay === 'number') {

} else if (typeof options.delay === 'function') {
nextDelay = options.delay(usedAttempts, ...innerArgs);
nextDelay = options.delay(reason, usedAttempts, ...innerArgs);
}
if (shouldRetry) {
return promiseDelay(nextDelay)
.then(() => attempt(...newArguments))
.catch((subReason: any) => Promise.reject(subReason));
} else {
return Promise.reject(reason);
}
return Promise.all([shouldRetry, nextDelay, newArguments]).then(
([resolvedShouldRetry, resolvedNextDelay, resolvedNewArguments = []]) => {
if (resolvedShouldRetry) {
return promiseDelay(resolvedNextDelay)
.then(() => attempt(...resolvedNewArguments))
.catch((subReason: any) => Promise.reject(subReason));
} else {
return Promise.reject(reason);
}
},
);
});

@@ -50,0 +54,0 @@ }(...args);

{
"name": "promise-again",
"version": "1.2.0",
"version": "1.3.0",
"main": "./dist/umd/index.js",

@@ -5,0 +5,0 @@ "module": "./dist/es6/index.js",

# promise-again
Composing util method which reruns promise-returning function with possible delay and arguments interceptor
Yet another wrapper for functions that return promise to retry rejected attempts.
But this one with advanced **flexibility**.
---
## Usage
```
import promiseAgain from 'promise-again';
function someFunctionReturningAPromise() {
...
}
const wrappedFunction = promiseAgain(
/**
* A function that returns a promise
**/
someFunctionReturningAPromise,
{
/**
* Optional. Delay in milisecconds or a function that returns a delay
* or a function that returns a promise that resolves to a delay.
*
* @param reason - reason of the last rejection;
* @param attempt - number of used attempts;
* @param ...args - last attempt arguments;
*
* @returns {number | Promise<number>} - modified arguments to be used in the next attempt or a promise that is resolved to such arguments;
**/
delay: number | ((attempt: number, ...args: any[]) => number | Promise<number>);
/**
* Required. Number of attempts or function that returns true or a Promise that resolved to true if retry is needed;
*
* @param reason - reason of the last rejection;
* @param attempt - number of used attempts;
* @param ...args - last attempt arguments;
*
* @returns {boolean | Promise<any[]>} - modified arguments to be used in the next attempt or a promise that is resolved to such arguments;
**/
attempts: number | ((attempt: number, ...args: any[]) => boolean | Promise<boolean>);
/**
* Optional. Function that is called before every retry attempt to modify next attempt arguments;
*
* @param reason - reason of the last rejection;
* @param attempt - number of used attempts;
* @param ...args - last attempt arguments;
*
* @returns {any[] | Promise<any[]>} - modified arguments to be used in the next attempt or a promise that is resolved to such arguments;
**/
retryArgumentsInterceptor: (reason: any, attempt: number, ...args: any[]) => any[] | Promise<any[]>;
}
)
```

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

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