333
schangxiang@126.com
2025-09-19 18966e02fb573c7e2bb0c6426ed792b38b910940
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
'use strict';
 
const is = require('is-type-of');
 
const IS_READY = Symbol('isReady');
const READY_CALLBACKS = Symbol('readyCallbacks');
const READY_ARG = Symbol('readyArg');
 
class Ready {
 
  constructor() {
    this[IS_READY] = false;
    this[READY_CALLBACKS] = [];
  }
 
  ready(flagOrFunction) {
    // register a callback
    if (flagOrFunction === undefined || is.function(flagOrFunction)) {
      return this.register(flagOrFunction);
    }
    // emit callbacks
    this.emit(flagOrFunction);
  }
 
 
  /**
   * Register a callback to the callback stack, it will be called when emit.
   * It will return promise when no argument passing.
   * @param {Function|Undefined} func - a callback
   * @return {Undefined|Promise} promise
   */
  register(func) {
    // support `this.ready().then(onready);` and `yield this.ready()`;
    if (!func) {
      return new Promise((resolve, reject) => {
        function func(err) {
          if (err) {
            reject(err);
          } else {
            resolve();
          }
        }
        if (this[IS_READY]) {
          return func(this[READY_ARG]);
        }
        this[READY_CALLBACKS].push(func);
      });
    }
 
    // this.ready(fn)
    if (this[IS_READY]) {
      func(this[READY_ARG]);
    } else {
      this[READY_CALLBACKS].push(func);
    }
  }
 
  /**
   * Call the callbacks that has been registerd, and clean the callback stack.
   * If the flag is not false, it will be marked as ready. Then the callbacks will be called immediatly when register.
   * @param {Boolean|Error} flag - Set a flag whether it had been ready. If the flag is an error, it's also ready, but the callback will be called with argument `error`
   */
  emit(flag) {
    // this.ready(true);
    // this.ready(false);
    // this.ready(err);
    this[IS_READY] = flag !== false;
    this[READY_ARG] = flag instanceof Error ? flag : undefined;
    // this.ready(true)
    if (this[IS_READY]) {
      this[READY_CALLBACKS]
        .splice(0, Infinity)
        .forEach(callback => process.nextTick(() => callback(this[READY_ARG])));
    }
  }
 
  /**
   * @param {Object} obj - an object that be mixed
   */
  static mixin(obj) {
    if (!obj) return;
    const ready = new Ready();
    // delegate method
    obj.ready = flagOrFunction => ready.ready(flagOrFunction);
  }
}
 
function mixin(object) {
  Ready.mixin(object);
}
 
module.exports = mixin;
module.exports.mixin = mixin;
module.exports.Ready = Ready;