Require Node.js 8, add TypeScript definition (#10)
This commit is contained in:
parent
db65c4b511
commit
9a429bc248
|
@ -1,2 +1 @@
|
||||||
* text=auto
|
* text=auto eol=lf
|
||||||
*.js text eol=lf
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
language: node_js
|
language: node_js
|
||||||
node_js:
|
node_js:
|
||||||
|
- '10'
|
||||||
- '8'
|
- '8'
|
||||||
- '6'
|
|
||||||
- '4'
|
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
/**
|
||||||
|
* Timeout a promise after a specified amount of time.
|
||||||
|
*
|
||||||
|
* If you pass in a cancelable promise, specifically a promise with a `.cancel()` method, that method will be called when the `pTimeout` promise times out.
|
||||||
|
*
|
||||||
|
* @param input - Promise to decorate.
|
||||||
|
* @param ms - Milliseconds before timing out.
|
||||||
|
* @param message - Specify a custom error message or error. If you do a custom error, it's recommended to sub-class `pTimeout.TimeoutError`. Default: `'Promise timed out after 50 milliseconds'`.
|
||||||
|
* @returns A decorated `input` that times out after `ms` time.
|
||||||
|
*/
|
||||||
|
export default function pTimeout<ValueType>(
|
||||||
|
input: PromiseLike<ValueType>,
|
||||||
|
ms: number,
|
||||||
|
message?: string | Error
|
||||||
|
): Promise<ValueType>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Timeout a promise after a specified amount of time.
|
||||||
|
*
|
||||||
|
* If you pass in a cancelable promise, specifically a promise with a `.cancel()` method, that method will be called when the `pTimeout` promise times out.
|
||||||
|
*
|
||||||
|
* @param input - Promise to decorate.
|
||||||
|
* @param ms - Milliseconds before timing out.
|
||||||
|
* @param fallback - Do something other than rejecting with an error on timeout. You could for example retry.
|
||||||
|
* @returns A decorated `input` that times out after `ms` time.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
*
|
||||||
|
* import delay from 'delay';
|
||||||
|
* import pTimeout from 'p-timeout';
|
||||||
|
*
|
||||||
|
* const delayedPromise = () => delay(200);
|
||||||
|
*
|
||||||
|
* pTimeout(delayedPromise(), 50, () => {
|
||||||
|
* return pTimeout(delayedPromise(), 300);
|
||||||
|
* });
|
||||||
|
*/
|
||||||
|
export default function pTimeout<ValueType, ReturnType>(
|
||||||
|
input: PromiseLike<ValueType>,
|
||||||
|
ms: number,
|
||||||
|
fallback: () => ReturnType | Promise<ReturnType>
|
||||||
|
): Promise<ValueType | ReturnType>;
|
||||||
|
|
||||||
|
export class TimeoutError extends Error {
|
||||||
|
readonly name: 'TimeoutError';
|
||||||
|
constructor(message?: string);
|
||||||
|
}
|
16
index.js
16
index.js
|
@ -1,4 +1,5 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const pFinally = require('p-finally');
|
const pFinally = require('p-finally');
|
||||||
|
|
||||||
class TimeoutError extends Error {
|
class TimeoutError extends Error {
|
||||||
|
@ -8,7 +9,7 @@ class TimeoutError extends Error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = (promise, ms, fallback) => new Promise((resolve, reject) => {
|
const pTimeout = (promise, ms, fallback) => new Promise((resolve, reject) => {
|
||||||
if (typeof ms !== 'number' || ms < 0) {
|
if (typeof ms !== 'number' || ms < 0) {
|
||||||
throw new TypeError('Expected `ms` to be a positive number');
|
throw new TypeError('Expected `ms` to be a positive number');
|
||||||
}
|
}
|
||||||
|
@ -17,23 +18,25 @@ module.exports = (promise, ms, fallback) => new Promise((resolve, reject) => {
|
||||||
if (typeof fallback === 'function') {
|
if (typeof fallback === 'function') {
|
||||||
try {
|
try {
|
||||||
resolve(fallback());
|
resolve(fallback());
|
||||||
} catch (err) {
|
} catch (error) {
|
||||||
reject(err);
|
reject(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const message = typeof fallback === 'string' ? fallback : `Promise timed out after ${ms} milliseconds`;
|
const message = typeof fallback === 'string' ? fallback : `Promise timed out after ${ms} milliseconds`;
|
||||||
const err = fallback instanceof Error ? fallback : new TimeoutError(message);
|
const timeoutError = fallback instanceof Error ? fallback : new TimeoutError(message);
|
||||||
|
|
||||||
if (typeof promise.cancel === 'function') {
|
if (typeof promise.cancel === 'function') {
|
||||||
promise.cancel();
|
promise.cancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
reject(err);
|
reject(timeoutError);
|
||||||
}, ms);
|
}, ms);
|
||||||
|
|
||||||
pFinally(
|
pFinally(
|
||||||
|
// eslint-disable-next-line promise/prefer-await-to-then
|
||||||
promise.then(resolve, reject),
|
promise.then(resolve, reject),
|
||||||
() => {
|
() => {
|
||||||
clearTimeout(timer);
|
clearTimeout(timer);
|
||||||
|
@ -41,4 +44,7 @@ module.exports = (promise, ms, fallback) => new Promise((resolve, reject) => {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
module.exports = pTimeout;
|
||||||
|
module.exports.default = pTimeout;
|
||||||
|
|
||||||
module.exports.TimeoutError = TimeoutError;
|
module.exports.TimeoutError = TimeoutError;
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
import {expectType} from 'tsd-check';
|
||||||
|
import pTimeout, {TimeoutError} from '.';
|
||||||
|
|
||||||
|
const delayedPromise: () => Promise<string> = () =>
|
||||||
|
new Promise(resolve => setTimeout(() => resolve('foo'), 200));
|
||||||
|
|
||||||
|
pTimeout(delayedPromise(), 50).then(() => 'foo');
|
||||||
|
pTimeout(delayedPromise(), 50, () => {
|
||||||
|
return pTimeout(delayedPromise(), 300);
|
||||||
|
});
|
||||||
|
pTimeout(delayedPromise(), 50).then(value => expectType<string>(value));
|
||||||
|
pTimeout(delayedPromise(), 50, 'error').then(value =>
|
||||||
|
expectType<string>(value)
|
||||||
|
);
|
||||||
|
pTimeout(delayedPromise(), 50, new Error('error')).then(value =>
|
||||||
|
expectType<string>(value)
|
||||||
|
);
|
||||||
|
pTimeout(delayedPromise(), 50, async () => 10).then(value => {
|
||||||
|
expectType<string | number>(value);
|
||||||
|
});
|
||||||
|
pTimeout(delayedPromise(), 50, () => 10).then(value => {
|
||||||
|
expectType<string | number>(value);
|
||||||
|
});
|
||||||
|
|
||||||
|
expectType<typeof TimeoutError>(TimeoutError);
|
16
package.json
16
package.json
|
@ -10,13 +10,14 @@
|
||||||
"url": "sindresorhus.com"
|
"url": "sindresorhus.com"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=4"
|
"node": ">=8"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "xo && ava"
|
"test": "xo && ava && tsd-check"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"index.js"
|
"index.js",
|
||||||
|
"index.d.ts"
|
||||||
],
|
],
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"promise",
|
"promise",
|
||||||
|
@ -35,9 +36,10 @@
|
||||||
"p-finally": "^1.0.0"
|
"p-finally": "^1.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"ava": "*",
|
"ava": "^1.3.1",
|
||||||
"delay": "^2.0.0",
|
"delay": "^4.1.0",
|
||||||
"p-cancelable": "^0.3.0",
|
"p-cancelable": "^1.1.0",
|
||||||
"xo": "*"
|
"tsd-check": "^0.3.0",
|
||||||
|
"xo": "^0.24.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
29
test.js
29
test.js
|
@ -1,49 +1,50 @@
|
||||||
import test from 'ava';
|
import test from 'ava';
|
||||||
import delay from 'delay';
|
import delay from 'delay';
|
||||||
import PCancelable from 'p-cancelable';
|
import PCancelable from 'p-cancelable';
|
||||||
import m from '.';
|
import pTimeout from '.';
|
||||||
|
|
||||||
const fixture = Symbol('fixture');
|
const fixture = Symbol('fixture');
|
||||||
const fixtureErr = new Error('fixture');
|
const fixtureErr = new Error('fixture');
|
||||||
|
|
||||||
test('resolves before timeout', async t => {
|
test('resolves before timeout', async t => {
|
||||||
t.is(await m(delay(50).then(() => fixture), 200), fixture);
|
t.is(await pTimeout(delay(50).then(() => fixture), 200), fixture);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('throws when ms is not number', async t => {
|
test('throws when ms is not number', async t => {
|
||||||
await t.throws(m(delay(50), '200'), TypeError);
|
await t.throwsAsync(pTimeout(delay(50), '200'), TypeError);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('throws when ms is negative number', async t => {
|
test('throws when ms is negative number', async t => {
|
||||||
await t.throws(m(delay(50), -1), TypeError);
|
await t.throwsAsync(pTimeout(delay(50), -1), TypeError);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('rejects after timeout', async t => {
|
test('rejects after timeout', async t => {
|
||||||
await t.throws(m(delay(200), 50), m.TimeoutError);
|
await t.throwsAsync(pTimeout(delay(200), 50), pTimeout.TimeoutError);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('rejects before timeout if specified promise rejects', async t => {
|
test('rejects before timeout if specified promise rejects', async t => {
|
||||||
await t.throws(m(delay(50).then(() => Promise.reject(fixtureErr)), 200), fixtureErr.message);
|
await t.throwsAsync(pTimeout(delay(50).then(() => Promise.reject(fixtureErr)), 200), fixtureErr.message);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('fallback argument', async t => {
|
test('fallback argument', async t => {
|
||||||
await t.throws(m(delay(200), 50, 'rainbow'), 'rainbow');
|
await t.throwsAsync(pTimeout(delay(200), 50, 'rainbow'), 'rainbow');
|
||||||
await t.throws(m(delay(200), 50, new RangeError('cake')), RangeError);
|
await t.throwsAsync(pTimeout(delay(200), 50, new RangeError('cake')), RangeError);
|
||||||
await t.throws(m(delay(200), 50, () => Promise.reject(fixtureErr)), fixtureErr.message);
|
await t.throwsAsync(pTimeout(delay(200), 50, () => Promise.reject(fixtureErr)), fixtureErr.message);
|
||||||
await t.throws(m(delay(200), 50, () => {
|
await t.throwsAsync(pTimeout(delay(200), 50, () => {
|
||||||
throw new RangeError('cake');
|
throw new RangeError('cake');
|
||||||
}), RangeError);
|
}), RangeError);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('calls `.cancel()` on promise when it exists', async t => {
|
test('calls `.cancel()` on promise when it exists', async t => {
|
||||||
const p = new PCancelable(onCancel => {
|
const promise = new PCancelable(async (resolve, reject, onCancel) => {
|
||||||
onCancel(() => {
|
onCancel(() => {
|
||||||
t.pass();
|
t.pass();
|
||||||
});
|
});
|
||||||
|
|
||||||
return delay(200);
|
await delay(200);
|
||||||
|
resolve();
|
||||||
});
|
});
|
||||||
|
|
||||||
await t.throws(m(p, 50), m.TimeoutError);
|
await t.throwsAsync(pTimeout(promise, 50), pTimeout.TimeoutError);
|
||||||
t.true(p.canceled);
|
t.true(promise.isCanceled);
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue