'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;
|