index.js 1.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657
  1. 'use strict';
  2. const pFinally = require('p-finally');
  3. class TimeoutError extends Error {
  4. constructor(message) {
  5. super(message);
  6. this.name = 'TimeoutError';
  7. }
  8. }
  9. const pTimeout = (promise, milliseconds, fallback) => new Promise((resolve, reject) => {
  10. if (typeof milliseconds !== 'number' || milliseconds < 0) {
  11. throw new TypeError('Expected `milliseconds` to be a positive number');
  12. }
  13. if (milliseconds === Infinity) {
  14. resolve(promise);
  15. return;
  16. }
  17. const timer = setTimeout(() => {
  18. if (typeof fallback === 'function') {
  19. try {
  20. resolve(fallback());
  21. } catch (error) {
  22. reject(error);
  23. }
  24. return;
  25. }
  26. const message = typeof fallback === 'string' ? fallback : `Promise timed out after ${milliseconds} milliseconds`;
  27. const timeoutError = fallback instanceof Error ? fallback : new TimeoutError(message);
  28. if (typeof promise.cancel === 'function') {
  29. promise.cancel();
  30. }
  31. reject(timeoutError);
  32. }, milliseconds);
  33. // TODO: Use native `finally` keyword when targeting Node.js 10
  34. pFinally(
  35. // eslint-disable-next-line promise/prefer-await-to-then
  36. promise.then(resolve, reject),
  37. () => {
  38. clearTimeout(timer);
  39. }
  40. );
  41. });
  42. module.exports = pTimeout;
  43. // TODO: Remove this for the next major release
  44. module.exports.default = pTimeout;
  45. module.exports.TimeoutError = TimeoutError;