Appearance
SyncLoopHock
同步循环执行
特点
- 串行同步执行
- 事件处理函数返回 true 表示继续循环,如果返回 undefined 的话,表示结束循环。
例子
js
const { SyncLoopHook } = require("tapable")
// 创建一个同步事件流实例对象
const h1 = new SyncLoopHook(["name", "age"])
let timer = 0
h1.intercept({
call: (source, target, routesList) => {
console.log("Starting to SyncLoopHook")
},
register: tapInfo => {
console.log(`${tapInfo.name} is doing its job`)
return tapInfo
},
tap: tap => {
console.log(tap.name)
},
})
// 添加同步事件A
h1.tap("A", function (name, age) {
console.log("A", name)
console.log("A", age)
timer++
if (timer == 10) {
return undefined
} else {
return true
}
})
// 添加同步事件B
h1.tap("B", function (name, age) {
console.log("B", name)
console.log("B", age)
})
// 添加同步事件B
h1.tap("C", function (name, age) {
console.log("C", name)
console.log("C", age)
})
// 添加同步事件F
h1.tap(
{
name: "F",
before: "D",
},
function (name, age) {
console.log("F", name)
console.log("F", age)
console.log("F : timer", timer)
timer++
if (timer == 4) {
return undefined
} else {
return true
}
}
)
// 添加同步事件E
h1.tap(
{
name: "E",
before: "C",
},
function (name, age) {
console.log("E", name)
console.log("E", age)
}
)
// 添加同步事件D
h1.tap("D", function (name, age) {
console.log("D", name)
console.log("D", age)
})
// 执行这个事件流
h1.call("gzh", 20)
看上面的例子我们想到的输出结果是不是
F , F , F , F , F , A , A , A , A , A , A , B , E , C , D
但是实际上其进入的是死循环
为什么? 我们看上面 tapable 可执行的代码字符串是什么。
我们找到 SyncLoopHook 的 content
js
class SyncLoopHookCodeFactory extends HookCodeFactory {
content({ onError, onResult, onDone, rethrowIfPossible }) {
return this.callTapsLooping({
onError: (i, err) => onError(err),
onDone,
rethrowIfPossible,
})
}
}
发现其不是使用的 callTapsSeries 而是使用的 callTapsLooping,那我们先看一下 callTapsLooping
js
class HookCodeFactory {
callTapsLooping({ onError, onDone, rethrowIfPossible }) {
if (this.options.taps.length === 0) return onDone()
const syncOnly = this.options.taps.every(t => t.type === "sync")
let code = ""
if (!syncOnly) {
code += "var _looper = () => {\n"
code += "var _loopAsync = false;\n"
}
code += "var _loop;\n"
code += "do {\n"
code += "_loop = false;\n"
// 处理每一个拦截器的 loop 方法
for (let i = 0; i < this.options.interceptors.length; i++) {
const interceptor = this.options.interceptors[i]
if (interceptor.loop) {
code += `${this.getInterceptor(i)}.loop(${this.args({
before: interceptor.context ? "_context" : undefined,
})});\n`
}
}
code += this.callTapsSeries({
onError,
onResult: (i, result, next, doneBreak) => {
let code = ""
code += `if(${result} !== undefined) {\n`
code += "_loop = true;\n"
if (!syncOnly) code += "if(_loopAsync) _looper();\n"
code += doneBreak(true)
code += `} else {\n`
code += next()
code += `}\n`
return code
},
onDone:
onDone &&
(() => {
let code = ""
code += "if(!_loop) {\n"
code += onDone()
code += "}\n"
return code
}),
rethrowIfPossible: rethrowIfPossible && syncOnly,
})
code += "} while(_loop);\n"
if (!syncOnly) {
code += "_loopAsync = true;\n"
code += "};\n"
code += "_looper();\n"
}
return code
}
}
发现其也是通过 this.callTapsSeries() 只是其不可以简简单单的在每一个事件流的后面通过 onResult 去包装一层,其需要在整体外面包装一层 do { } while (_loop);
,然后在每一个事件流最后添加一个 if (_result0 !== undefined) {_loop = true;} else {}
这样只要 返回值不是 undefined 那么就不会执行下面的代码(事件流),且 _loop = true;触发do while
js
do {
_loop = false
if (_result0 !== undefined) {
_loop = true
} else {
// 下一个
if (_result0 !== undefined) {
_loop = true
} else {
}
}
} while (_loop)
我们看一下一个生成的代码
js
;(function anonymous(name, age) {
"use strict"
var _context
var _x = this._x
var _taps = this.taps
var _interceptors = this.interceptors
_interceptors[0].call(name, age)
var _loop
do {
_loop = false
var _tap0 = _taps[0]
_interceptors[0].tap(_tap0)
var _fn0 = _x[0]
var _result0 = _fn0(name, age)
if (_result0 !== undefined) {
_loop = true
} else {
var _tap1 = _taps[1]
_interceptors[0].tap(_tap1)
var _fn1 = _x[1]
var _result1 = _fn1(name, age)
if (_result1 !== undefined) {
_loop = true
} else {
var _tap2 = _taps[2]
_interceptors[0].tap(_tap2)
var _fn2 = _x[2]
var _result2 = _fn2(name, age)
if (_result2 !== undefined) {
_loop = true
} else {
var _tap3 = _taps[3]
_interceptors[0].tap(_tap3)
var _fn3 = _x[3]
var _result3 = _fn3(name, age)
if (_result3 !== undefined) {
_loop = true
} else {
var _tap4 = _taps[4]
_interceptors[0].tap(_tap4)
var _fn4 = _x[4]
var _result4 = _fn4(name, age)
if (_result4 !== undefined) {
_loop = true
} else {
var _tap5 = _taps[5]
_interceptors[0].tap(_tap5)
var _fn5 = _x[5]
var _result5 = _fn5(name, age)
if (_result5 !== undefined) {
_loop = true
} else {
if (!_loop) {
}
}
}
}
}
}
}
} while (_loop)
})
可见前面的我们 输出 5 次 F 是对的,因为这时候 timer < 4 所以其一直执行第一个循环,然后等于 4 的时候,F 返回 undefined 了,进入下一个事件流了(A),但是我们看上面的代码发现其不是下一个循环只是执行 A 而是从新从头执行一次,但是这时候 timer === 5 了那么 F 中 timer 永远不会等于 4 了,然后就无限执行 F.
总结
想要进入下一个事件流其返回的不是 false 而是 undefined
其在中间事件流触发循环不是只是循环执行当前事件流,而是从头开始执行整个事件流