参考浏览器事件派发实现一个同步事件流
同步事件流:
串行同步执行,上一个事件处理函数的返回值作为参数传递给下一个事件处理函数
在函数中可以终止该阶段后续函数的执行,也可以终止整个流程;
// 同步流程控制 function SyncProcess(hooks) { // 所处阶段 this.phase = null; // 最终停止冒泡的阶段 this.bubPhase = null; // 阶段返回值 this.returnValue = null; // 是否停止某同一类型事件 this.cancel = false; // 是否停止所有事件 this.cancelAll = false; // 流程节点 this.hooks = hooks || []; // 节点的函数列表, 假定都是同步任务 this.listeners = {} } SyncProcess.prototype.run = function (initData, initHook) { var returnValue = initData || null; var initHook = this.hooks.indexOf(initHook || null); var i = initHook === -1 ? 0 : initHook; for (; i < this.hooks.length; i++) { if (this.cancelAll) { break; } var listeners = this.listeners[this.hooks[i]] || []; this.phase = this.hooks[i]; for (var k = 0; k < listeners.length; k++) { returnValue = listeners[k].call(null, returnValue, this); if (this.cancelAll || (this.cancel && this.bubPhase === this.phase)) { break } } } this.returnValue = returnValue; return this } SyncProcess.prototype.registerHook = function (hook, index, callback) { if (typeof index === 'function') { callback = index; index = this.hooks.length } if (this.hooks.indexOf(hook) === -1) { this.hooks.splice(index, 0, hook) } if (hook in this.listeners) { this.listeners[hook].push(callback) } else { this.listeners[hook] = [callback] } } SyncProcess.prototype.removeHook = function (hook, callback) { var index = this.hooks.indexOf(hook) if (index === -1) { return } else { this.hooks.splice(index, 1); if (typeof callback === 'function') { var fnIndex = this.listeners[hook].indexOf(callback); if (fnIndex !== -1) { this.listeners[hook].splice(fnIndex, 1) } } else { delete this.listeners[hook] } } } SyncProcess.prototype.stopPrograh = function () { this.bubPhase = this.phase this.cancel = true } SyncProcess.prototype.stopProcess = function () { this.cancelAll = true; } SyncProcess.prototype.continueProcess = function () { this.cancelAll = false; var curStep = this.hooks.indexOf(this.phase); if (curStep == this.hooks.length - 1) { return } this.run(this.returnValue, this.hooks[curStep + 1]) } // 焖鸡 var mj = { "准备": [ function (v) { return v += '+土豆;' }, function (v) { return v += '+洋葱;' }, function (v) { return v += '+鸡肉;' } ], "切菜": [ function (v) { // if (v.indexOf('没肉啊') === -1) { // this.stopPrograh() // } return v += '+切菜 1;' }, function (v, pross) { pross.stopProcess(); return v += '+切菜 2;'; } ], "烹饪": [ function (v) { return v += '+大火爆炒 鸡肉;' }, function (v) { return v += '+加配菜焖5分钟;' } ], "出锅": [ function (v) { return v += '+加葱花 出锅;' } ] } var gx = Object.keys(mj); var cook = new SyncProcess(gx); gx.forEach(function (gx) { mj[gx].forEach(function (v) { cook.registerHook(gx, v) }) }); cook.registerHook('凉拌', 3, function (v) { return v + '搬一下:::' }) cook.run(); setTimeout(function () { cook.continueProcess(); console.log(cook); }, 2000) console.log(cook);
同步流程只是理想情况, 大多数还是异步流程;从流程的角度来说, 我们写程序时也是有很多的过程,把过程进行分类,排列,可以让程序结构更为清晰易懂;
更多同步或异步流程的实现, 可以参考 tapable;附: https://www.cnblogs.com/tugenhua0707/p/11317557.html
注:
浏览器的事件派发: 根据事件注册的阶段, 类型, 依照阶段和优先级顺序执行; 如果在某个函数中调用了 stopPropagation, 则停止父级同类型事件的传播; 如果调用了 stopImmediatePropagation,则
停止父级同类型事件的传播, 并且停止当前对象上注册的同类型的其他事件的传播;
举个例子:
给 innerDom 的 mouseenter事件类型注册了事件 fn1, fn2, 给父级 outerDom 的mouseenter类型注册了事件 pfn1, pfn2;
如果在 fn1 中调用了 stopPropagation, 则停止 mouseenter类型事件的冒泡, outerDom上为 mouseenter类型注册的事件 pfn1, pfn2 都不会被执行
如果在 fn1 中调用了 stopImmediatePropagation, 则停止 mouseenter类型事件的冒泡, outerDom上为 mouseenter类型注册的事件 pfn1, pfn2 都不会被执行,并且innerDom 上为 mouseenter类型事件注册的其他函数 fn2 也不会被执行