task.js 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. 'use strict';
  2. const util = require('util');
  3. const Events = require('events');
  4. const Timer = require('./timer');
  5. const { define, noop } = require('./utils');
  6. class Task extends Events {
  7. constructor(task = {}) {
  8. if (typeof task.name !== 'string') {
  9. throw new TypeError('expected task name to be a string');
  10. }
  11. super();
  12. define(this, 'isTask', true);
  13. define(this, 'app', task.app);
  14. this.name = task.name;
  15. this.status = 'pending';
  16. this.options = Object.assign({ deps: [] }, task.options);
  17. this.callback = task.callback || noop;
  18. this.deps = [...task.deps || [], ...this.options.deps];
  19. this.time = new Timer();
  20. if (this.setMaxListeners) {
  21. this.setMaxListeners(0);
  22. }
  23. }
  24. [util.inspect.custom]() {
  25. return `<Task "${this.name}" deps: [${this.deps.join(', ')}]>`;
  26. }
  27. run(options) {
  28. let finished = false;
  29. let orig = Object.assign({}, this.options);
  30. this.options = Object.assign({}, this.options, options);
  31. this.status = 'preparing';
  32. this.emit('preparing', this);
  33. if (this.skip(options)) {
  34. return () => Promise.resolve(null);
  35. }
  36. this.time = new Timer();
  37. this.time.start();
  38. this.status = 'starting';
  39. this.emit('starting', this);
  40. return () => new Promise(async(resolve, reject) => {
  41. const finish = (err, value) => {
  42. if (finished) return;
  43. finished = true;
  44. try {
  45. this.options = orig;
  46. this.time.end();
  47. this.status = 'finished';
  48. this.emit('finished', this);
  49. if (err) {
  50. define(err, 'task', this);
  51. reject(err);
  52. this.emit('error', err);
  53. } else {
  54. resolve(value);
  55. }
  56. } catch (err) {
  57. reject(err);
  58. }
  59. };
  60. try {
  61. if (typeof this.callback !== 'function') {
  62. finish();
  63. return;
  64. }
  65. let res = this.callback.call(this, finish);
  66. if (res instanceof Promise) {
  67. let val = await res;
  68. if (val) res = val;
  69. }
  70. if (isEmitter(res)) {
  71. res.on('error', finish);
  72. res.on('finish', finish);
  73. res.on('end', finish);
  74. return;
  75. }
  76. if (this.callback.length === 0) {
  77. if (res && res.then) {
  78. res.then(() => finish());
  79. } else {
  80. finish(null, res);
  81. }
  82. }
  83. } catch (err) {
  84. finish(err);
  85. }
  86. });
  87. }
  88. skip(options) {
  89. let app = this.app || {};
  90. let opts = Object.assign({}, app.options, this.options, options);
  91. if (opts.run === false) {
  92. return true;
  93. }
  94. if (Array.isArray(opts.skip)) {
  95. return opts.skip.includes(this.name);
  96. }
  97. switch (typeof opts.skip) {
  98. case 'boolean':
  99. return opts.skip === true;
  100. case 'function':
  101. return opts.skip(this) === true;
  102. case 'string':
  103. return opts.skip === this.name;
  104. default: {
  105. return false;
  106. }
  107. }
  108. }
  109. }
  110. function isEmitter(val) {
  111. return val && (typeof val.on === 'function' || typeof val.pipe === 'function');
  112. }
  113. module.exports = Task;