AsyncExit.js 2.8 KB

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