Appearance
AsyncParallelBailHook
AsyncParallelBailHook 为异步并行执行的。和我们上面的 AsyncParallelHook 一样,通过使用 tapAsync 注册事件,通过 callAsync 触发事件,也可以通过 tapPromise 注册事件,使用 promise 来触发。
其与其他的 AsyncParallelHook 不同的地方在于只要有一个在 done(undefined,true),就会执行整个的回调,但是不阻止后面事件流的执行和回调
例子
js
const { AsyncParallelBailHook } = require("tapable")
// 创建一个同步事件流实例对象
const h1 = new AsyncParallelBailHook(["xxx", "arg2"])
h1.intercept({
call: (source, target, routesList) => {
console.log("Starting to h1 routes")
},
register: tapInfo => {
// tapInfo = { type: "promise", name: "GoogleMapsPlugin", fn: ... }
console.log(`${tapInfo.name} is doing its job`)
return tapInfo // may return a new tapInfo object
},
tap: tap => {
console.log(tap, "444444")
},
})
// 添加同步事件A
h1.tapAsync("A", (name, age, done) => {
setTimeout(() => {
console.log("A", name, age, new Date().getSeconds())
done(undefined, true)
}, 2000)
})
// 添加同步事件B
h1.tapAsync("B", (name, age, done) => {
setTimeout(() => {
console.log("B", name, age, new Date().getSeconds())
done()
}, 3000)
})
// 添加同步事件C
h1.tapAsync("C", (name, age, done) => {
setTimeout(() => {
console.log("C", name, age, new Date().getSeconds())
done()
}, 2000)
})
// 添加同步事件F
h1.tapAsync(
{
name: "F",
before: "D",
},
(name, age, done) => {
setTimeout(() => {
console.log("F", name, age, new Date().getSeconds())
done()
}, 2000)
}
)
// 添加同步事件E
h1.tapAsync(
{
name: "E",
before: "C",
},
(name, age, done) => {
setTimeout(() => {
console.log("E", name, age, new Date().getSeconds())
done()
}, 2000)
}
)
// 添加同步事件D
h1.tapAsync("D", (name, age, done) => {
setTimeout(() => {
console.log("D", name, age, new Date().getSeconds())
done()
}, 2000)
})
// 执行这个事件流
h1.callAsync("立早", 20, () => {
console.log("函数执行完毕")
})
结果为
[HMR] Waiting for update signal from WDS...
A is doing its job
B is doing its job
C is doing its job
F is doing its job
E is doing its job
D is doing its job
Starting to h1 routes
{type: "async", fn: ƒ, name: "F", before: "D"} "444444"
{type: "async", fn: ƒ, name: "A"} "444444"
{type: "async", fn: ƒ, name: "B"} "444444"
{type: "async", fn: ƒ, name: "E", before: "C"} "444444"
{type: "async", fn: ƒ, name: "C"} "444444"
{type: "async", fn: ƒ, name: "D"} "444444"
[WDS] Hot Module Replacement enabled.
F 立早 20 30
A 立早 20 30
函数执行完毕
E 立早 20 30
C 立早 20 30
D 立早 20 30
B 立早 20 31
发现其秒数都是 30 秒(31 是延迟 3 秒),那么说明所有的事件流是并行执行的。
其中在 A 中 done(undefined , 'gzhA');
就直接执行了整个钩子的回调 done()
原理
this.content()
js
class AsyncSeriesBailHookCodeFactory extends HookCodeFactory {
content({ onError, onResult, onDone }) {
return this.callTapsSeries({
onError: (i, err, next, doneBreak) => onError(err) + doneBreak(true),
onResult: (i, result, next) => `if(${result} !== undefined) {\n${onResult(result)};\n} else {\n${next()}}\n`,
onDone,
})
}
}
全部伪代码
js
var _results = new Array(6)
var _checkDone = () => {
for (var i = 0; i < _results.length; i++) {
var item = _results[i]
if (item === undefined) return false
if (item.result !== undefined) {
_callback(null, item.result)
return true
}
if (item.error) {
_callback(item.error)
return true
}
}
return false
}
do {
var _counter = 6
var _done = () => {
_callback()
}
if (_counter <= 0) break
var _tap0 = _taps[0]
_interceptors[0].tap(_tap0)
var _fn0 = _x[0]
_fn0(xxx, arg2, (_err0, _result0) => {
if (_err0) {
if (_counter > 0) {
if (0 < _results.length && ((_results.length = 1), (_results[0] = { error: _err0 }), _checkDone())) {
_counter = 0
} else {
if (--_counter === 0) _done()
}
}
} else {
if (_counter > 0) {
if (
0 < _results.length &&
(_result0 !== undefined && (_results.length = 1), (_results[0] = { result: _result0 }), _checkDone())
) {
_counter = 0
} else {
if (--_counter === 0) _done()
}
}
}
})
if (_counter <= 0) break
if (1 >= _results.length) {
if (--_counter === 0) _done()
} else {
var _tap1 = _taps[1]
_interceptors[0].tap(_tap1)
var _fn1 = _x[1]
_fn1(xxx, arg2, (_err1, _result1) => {
if (_err1) {
if (_counter > 0) {
if (1 < _results.length && ((_results.length = 2), (_results[1] = { error: _err1 }), _checkDone())) {
_counter = 0
} else {
if (--_counter === 0) _done()
}
}
} else {
if (_counter > 0) {
if (
1 < _results.length &&
(_result1 !== undefined && (_results.length = 2), (_results[1] = { result: _result1 }), _checkDone())
) {
_counter = 0
} else {
if (--_counter === 0) _done()
}
}
}
})
}
if (_counter <= 0) break
if (2 >= _results.length) {
if (--_counter === 0) _done()
} else {
var _tap2 = _taps[2]
_interceptors[0].tap(_tap2)
var _fn2 = _x[2]
_fn2(xxx, arg2, (_err2, _result2) => {
if (_err2) {
if (_counter > 0) {
if (2 < _results.length && ((_results.length = 3), (_results[2] = { error: _err2 }), _checkDone())) {
_counter = 0
} else {
if (--_counter === 0) _done()
}
}
} else {
if (_counter > 0) {
if (
2 < _results.length &&
(_result2 !== undefined && (_results.length = 3), (_results[2] = { result: _result2 }), _checkDone())
) {
_counter = 0
} else {
if (--_counter === 0) _done()
}
}
}
})
}
if (_counter <= 0) break
if (3 >= _results.length) {
if (--_counter === 0) _done()
} else {
var _tap3 = _taps[3]
_interceptors[0].tap(_tap3)
var _fn3 = _x[3]
_fn3(xxx, arg2, (_err3, _result3) => {
if (_err3) {
if (_counter > 0) {
if (3 < _results.length && ((_results.length = 4), (_results[3] = { error: _err3 }), _checkDone())) {
_counter = 0
} else {
if (--_counter === 0) _done()
}
}
} else {
if (_counter > 0) {
if (
3 < _results.length &&
(_result3 !== undefined && (_results.length = 4), (_results[3] = { result: _result3 }), _checkDone())
) {
_counter = 0
} else {
if (--_counter === 0) _done()
}
}
}
})
}
if (_counter <= 0) break
if (4 >= _results.length) {
if (--_counter === 0) _done()
} else {
var _tap4 = _taps[4]
_interceptors[0].tap(_tap4)
var _fn4 = _x[4]
_fn4(xxx, arg2, (_err4, _result4) => {
if (_err4) {
if (_counter > 0) {
if (4 < _results.length && ((_results.length = 5), (_results[4] = { error: _err4 }), _checkDone())) {
_counter = 0
} else {
if (--_counter === 0) _done()
}
}
} else {
if (_counter > 0) {
if (
4 < _results.length &&
(_result4 !== undefined && (_results.length = 5), (_results[4] = { result: _result4 }), _checkDone())
) {
_counter = 0
} else {
if (--_counter === 0) _done()
}
}
}
})
}
if (_counter <= 0) break
if (5 >= _results.length) {
if (--_counter === 0) _done()
} else {
var _tap5 = _taps[5]
_interceptors[0].tap(_tap5)
var _fn5 = _x[5]
_fn5(xxx, arg2, (_err5, _result5) => {
if (_err5) {
if (_counter > 0) {
if (5 < _results.length && ((_results.length = 6), (_results[5] = { error: _err5 }), _checkDone())) {
_counter = 0
} else {
if (--_counter === 0) _done()
}
}
} else {
if (_counter > 0) {
if (
5 < _results.length &&
(_result5 !== undefined && (_results.length = 6), (_results[5] = { result: _result5 }), _checkDone())
) {
_counter = 0
} else {
if (--_counter === 0) _done()
}
}
}
})
}
} while (false)
从上面可以看出其跟 asyncParallelHook 一样通过 _counter 的方式去处理并行异步回调完成的问题,但是跟 asyncParallelHook 不一样的地方在于其执行钩子的回调不是在所有的异步回调完成(_counter === 0),而是
在某一个异步中
done(undefined,true)
传入了第二个不为 undefined 的参数。某一个异步中有错误产生
done(error)
这两种情况都会触发钩子的回调
那么其是如何判断是哪一个事件流触发的钩子回调且保留其 err 信息
从上面伪代码上看其核心就是
js
// 初始化一个当前事件流个数空间大小的数组
var _results = new Array(6)
var _checkDone = () => {
for (var i = 0; i < _results.length; i++) {
var item = _results[i]
if (item === undefined) return false
// 如果是`done(undefined,true)` 第二个参数不是undefined 触发钩子回调
if (item.result !== undefined) {
_callback(null, item.result)
return true
}
// 如果是`done(error)` 第二个参数不是undefined 触发钩子回调
if (item.error) {
_callback(item.error)
return true
}
}
return false
}
可见其初始化一个当前事件流个数空间大小的数组,然后在每一个事件流回调中如果触发了 error
或者 第二个参数不是 undefined
都会去 _checkDone()
,然后遍历找到对应的对象 { error: xxx , result : 第二个参数的值 }