AsyncExit.js 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. const defaultTimeout = 15*1000;//15 sec
  2. const exitSignals = ['SIGINT', 'SIGTERM', 'SIGBREAK', 'SIGHUP', 'uncaughtException'];
  3. //singleton
  4. let instance = null;
  5. class AsyncExit {
  6. constructor(signals = exitSignals, codeOnSignal = 2) {
  7. if (!instance) {
  8. this.onSignalCallbacks = new Map();
  9. this.callbacks = new Map();
  10. this.afterCallbacks = new Map();
  11. this.exitTimeout = defaultTimeout;
  12. this._init(signals, codeOnSignal);
  13. instance = this;
  14. }
  15. return instance;
  16. }
  17. _init(signals, codeOnSignal) {
  18. const runSingalCallbacks = async(signal, err, origin) => {
  19. if (!this.onSignalCallbacks.size) {
  20. console.error(`Uncaught signal "${signal}" received, error: "${(err.stack ? err.stack : err)}"`);
  21. }
  22. for (const signalCallback of this.onSignalCallbacks.keys()) {
  23. try {
  24. await signalCallback(signal, err, origin);
  25. } catch(e) {
  26. console.error(e);
  27. }
  28. }
  29. };
  30. for (const signal of signals) {
  31. process.once(signal, async(err, origin) => {
  32. await runSingalCallbacks(signal, err, origin);
  33. this.exit(codeOnSignal);
  34. });
  35. }
  36. }
  37. onSignal(signalCallback) {
  38. if (!this.onSignalCallbacks.has(signalCallback)) {
  39. this.onSignalCallbacks.set(signalCallback, true);
  40. }
  41. }
  42. add(exitCallback) {
  43. if (!this.callbacks.has(exitCallback)) {
  44. this.callbacks.set(exitCallback, true);
  45. }
  46. }
  47. addAfter(exitCallback) {
  48. if (!this.afterCallbacks.has(exitCallback)) {
  49. this.afterCallbacks.set(exitCallback, true);
  50. }
  51. }
  52. remove(exitCallback) {
  53. if (this.callbacks.has(exitCallback)) {
  54. this.callbacks.delete(exitCallback);
  55. }
  56. if (this.afterCallbacks.has(exitCallback)) {
  57. this.afterCallbacks.delete(exitCallback);
  58. }
  59. }
  60. setExitTimeout(timeout) {
  61. this.exitTimeout = timeout;
  62. }
  63. exit(code = 0) {
  64. if (this.exiting)
  65. return;
  66. this.exiting = true;
  67. const timer = setTimeout(() => { process.exit(code); }, this.exitTimeout);
  68. (async() => {
  69. for (const exitCallback of this.callbacks.keys()) {
  70. try {
  71. await exitCallback();
  72. } catch(e) {
  73. console.error(e);
  74. }
  75. }
  76. for (const exitCallback of this.afterCallbacks.keys()) {
  77. try {
  78. await exitCallback();
  79. } catch(e) {
  80. console.error(e);
  81. }
  82. }
  83. clearTimeout(timer);
  84. //console.log('Exited gracefully');
  85. process.exit(code);
  86. })();
  87. }
  88. }
  89. module.exports = AsyncExit;