/**
* @file 事件队列管理
* @author lisfan <goolisfan@gmail.com>
* @version 1.0.0
* @licence MIT
*/
import validation from '@~lisfan/validation'
import Logger from '@~lisfan/logger'
// 存储主命名空间的常量
const PRIMARY_NAMESPACE = '__pri__'
const _actions = {
/**
* 分割队列命名空间
*
* @since 1.0.0
*
* @param {EventQueues} self - 本实本身
* @param {string} queueName - 队列名称
*
* @returns {Array}
*/
splitQueueName(self, queueName) {
// 命名空间进行处理
// 1. 处理空白符
// 2. 处理.符号多个的场景
// 3. 处理头和尾留有.符号的情况
// 4. 处理子命名空间空白符
const separator = self.$separator
const uniqSeparatorRegExp = new RegExp(`\${separator}+`, 'g')
// 提取中间内容
const extractContentRegExp = new RegExp(`^\${separator}?(${separator}*?)\${separator}?$`)
return queueName.trim()
.replace(uniqSeparatorRegExp, separator)
.replace(extractContentRegExp, '$1')
.split(separator).map((name) => {
return name.trim()
})
},
/**
* 初始化主命名空间集合
*
* @since 1.0.0
*
* @param {EventQueues} self - 本实本身
* @param {string} namespace - 主命名空间
*
* @returns {object}
*/
initMainNamespace(self, namespace) {
return !validation.isPlainObject(self.$queues[namespace])
? {}
: self.$queues[namespace]
},
/**
* 初始化子命名空间集合
*
* @since 1.0.0
*
* @param {EventQueues} self - 本实本身
* @param {string} mainNamespace - 主命名空间
* @param {string} subNamespace - 子命名空间
*
* @returns {object}
*/
initSubNamespace(self, mainNamespace, subNamespace) {
return !validation.isPlainObject(self.$queues[mainNamespace][subNamespace])
? {
events: [],
isAsync: []
}
: self.$queues[mainNamespace][subNamespace]
},
}
/**
* @classdesc
* 事件队列管理类
*
* @class
*/
class EventQueues {
/**
* 默认配置选项
*
* @since 1.0.0
*
* @static
* @readonly
* @memberOf EventQueues
*
* @type {object}
* @property {boolean} debug=false - 打印器调试模式是否开启
* @property {string} name='EventQueues' - 打印器名称标记
* @property {string} separator='.' - 子命名空间分割符
*/
static options = {
name: 'EventQueues',
debug: false,
separator: '.'
}
/**
* 更新默认配置选项
*
* @since 1.0.0
*
* @see EventQueues.options
*
* @param {object} options - 配置选项见{@link EventQueues.options}
*
* @returns {EventQueues}
*/
static config(options) {
EventQueues.options = {
...EventQueues.options,
...options
}
return EventQueues
}
/**
* 构造函数
*
* @see EventQueues.options
*
* @param {object} options - 配置选项见{@link EventQueues.options}
*/
constructor(options) {
this.$options = {
...EventQueues.options,
...options
}
this._logger = new Logger({
name: this.$options.name,
debug: this.$options.debug
})
}
/**
* 日志打印器,方便调试
*
* @since 1.0.0
*
* @private
*/
_logger = undefined
/**
* 实例初始配置项
*
* @since 1.0.0
*
* @readonly
*
* @type {object}
*/
$options = undefined
/**
* 事件队列集合
*
* @since 1.0.0
*
* @readonly
* @type {object}
*/
$queues = {}
/**
* 获取实例配置的分割符
*
* @since 1.0.0
*
* @getter
* @readonly
*
* @type {string}
*/
get $separator() {
return this.$options.separator
}
/**
* 获取打印器实例的名称标记
*
* @since 1.0.0
*
* @getter
* @readonly
*
* @type {string}
*/
get $name() {
return this._logger.$name
}
/**
* 获取实例的调试配置项
*
* @since 1.0.0
*
* @getter
* @readonly
*
* @type {boolean}
*/
get $debug() {
return this._logger.$debug
}
/**
* 绑定队列事件
*
* @since 1.0.0
*
* @param {string} name - 命名空间名称,支持多个子命名空间,用'.'号分隔,如mainname1.subname2.subname3
* @param {function} done - 事件句柄
* @param {boolean} [isAsync=false] - 是否为异步,如果是异步,则需要等待该事件执行完毕,再执行一个
* 如果异步事件的执行结果不依赖与上一个的执行结果,则可以不传入该字段
*
* @returns {EventQueues}
*/
on(name, done, isAsync = false) {
// 处理空白符
// 处理头和尾留有.符号的情况
// 处理.符号多个的场景
// 处理子命名空间空白符
const queuesNameList = _actions.splitQueueName(this, name)
const mainNamespace = queuesNameList[0]
this.$queues[mainNamespace] = _actions.initMainNamespace(this, mainNamespace)
// 如果事件只使用主命名空间定义,则定义在主命名空间下,
// 如果事件使用了子命名空间定义,则同时定义在主命名空间和子命名空间下
const queuesNamespaceList = [PRIMARY_NAMESPACE].concat(queuesNameList.slice(1))
queuesNamespaceList.forEach((subQueueName) => {
this.$queues[mainNamespace][subQueueName] = _actions.initSubNamespace(this, mainNamespace, subQueueName)
const subQueue = this.$queues[mainNamespace][subQueueName]
subQueue.events.push(done)
subQueue.isAsync.push(!!isAsync)
this._logger.log(`bind on success (${subQueueName})!`)
})
return this
}
/**
* 移除队列事件
*
* @since 1.0.0
*
* @param {string} name - 命名空间名称,支持多个子命名空间,用'.'号分隔,如mainname1.subname2.subname3
* @param {function} [done] - 移除指定的事件,若未指定,则移除该命名空间下所有事件队列
*
* @returns {EventQueues}
*/
off(name, done) {
const queuesNameList = _actions.splitQueueName(this, name)
const mainNamespace = queuesNameList[0]
// 该命名空间不存在
if (!validation.isPlainObject(this.$queues[mainNamespace])) {
this._logger.warn(`bind off faild! the (${mainNamespace}) main namespace is't exist.`)
return this
}
// 如果是移除主命名空间,则移除所有该主命名空间下的事件队列主队列
// 如果只移除子命名空间,则只移除该主命名空间下的该子命名空间
const queuesNamespaceList = [PRIMARY_NAMESPACE].concat(queuesNameList.slice(1))
// 如果queuesNamespaceList只有一项,则说明删除所有的事件队列
if (queuesNamespaceList.length === 1) {
// 存在命名空间
if (validation.isFunction(done)) {
// 指定了具体的事件时,只删除该指定的事件
const subQueue = this.$queues[mainNamespace][PRIMARY_NAMESPACE]
subQueue.events = subQueue.events.filter((cb) => {
return cb === done
})
this._logger.log(`bind off success (${PRIMARY_NAMESPACE})!`)
} else {
this.$queues[mainNamespace] = null
this._logger.log(`bind off success! remove all queues of the (${mainNamespace}) main namespace`)
}
return this
}
// 存在子命名空间时
queuesNamespaceList.forEach((subQueueName) => {
if (!validation.isPlainObject(this.$queues[mainNamespace][subQueueName])
|| !validation.isArray(this.$queues[mainNamespace][subQueueName].events)) {
this._logger.warn(`bind off faild! the (${subQueueName}) sub namespace is't exist.`)
return this
}
// 存在命名空间
if (validation.isFunction(done)) {
// 指定了具体的事件时,只删除该指定的事件
const subQueue = this.$queues[mainNamespace][subQueueName]
// 会删除该命名空间下所有该指定的事件
const isAsync = []
subQueue.events = subQueue.events.filter((cb, index) => {
if (cb === done) {
isAsync.push(subQueue.isAsync[index])
return true
}
})
subQueue.isAsync = isAsync
this._logger.log(`bind off success (${subQueueName})!`)
} else {
// 如果当前子命名空间是主命名空间不作删除
if (subQueueName === PRIMARY_NAMESPACE) {
return
}
this.$queues[mainNamespace][subQueueName] = null
this._logger.log(`bind off success! remove all queues of the (${subQueueName}) sub namespace`)
}
})
return this
}
/**
* 执行队列事件,上一个队列项的执行结果将作为下一个队列项的参数传入
*
* @since 1.0.0
*
* @async
*
* @param {string} name - 命名空间名称,支持多个子命名空间,用'.'号分隔,如mainname1.subname2.subname3
* @param {array} args - 参数列表,会将参数列表作为第一个事件队列的参数传入
*
* @returns {Promise}
*/
emit(name, ...args) {
return new Promise((resolve, reject) => {
try {
const queuesNameList = _actions.splitQueueName(this, name)
const mainNamespace = queuesNameList[0]
// 该命名空间不存在
if (!validation.isPlainObject(this.$queues[mainNamespace])) {
return resolve()
}
let queuesNamespaceList = queuesNameList.length === 1 ? [PRIMARY_NAMESPACE] : queuesNameList.slice(1)
// 执行后的最后结果
queuesNamespaceList.forEach((subQueueName) => {
// 不存在,则不执行
if (!validation.isPlainObject(this.$queues[mainNamespace][subQueueName])
|| !validation.isArray(this.$queues[mainNamespace][subQueueName].events)) {
return resolve()
}
let execResult = this.$queues[mainNamespace][subQueueName].events.reduce((result, done, index) => {
// 如果done不是函数,则将上一个结果继续返回
if (!validation.isFunction(done)) {
return result
}
return index === 0
? done.apply(null, args)
: done.call(null, result)
}, null)
return resolve(execResult)
})
} catch (err) {
return reject(err)
}
})
}
}
export default EventQueues