Appearance
AsyncParallelHook
AsyncParallelHook 为异步并行执行,如果是通过 tapAsync 注册的事件,那么我们需要通过 callAsync 触发,如果我们通过 tapPromise 注册的事件,那么我们需要 promise 触发。
例子
js
const { AsyncParallelHook } = require('tapable');
// 创建一个同步事件流实例对象
const h1 = new AsyncParallelHook(['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();
}, 2000);
});
// 添加同步事件B
h1.tapAsync('B', (name, age, done) => {
setTimeout(() => {
console.log('B', name, age, new Date().getSeconds());
done();
}, 2000);
});
// 添加同步事件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 55
A 立早 20 55
B 立早 20 55
E 立早 20 55
C 立早 20 55
D 立早 20 55
函数执行完毕
其秒数都是 55 秒执行的,说明所有的事件流都是在同一时间触发的
原理
this.content()
js
class AsyncParallelHookCodeFactory extends HookCodeFactory {
content({ onError, onDone }) {
return this.callTapsParallel({
onError: (i, err, done, doneBreak) => onError(err) + doneBreak(true),
onDone,
});
}
}
发现其不是通过 this.callTapSeries() 而是通过 callTapsParallel 去生成可以执行的代码的
js
callTapsParallel({ onError, onResult, onDone, rethrowIfPossible, onTap = (i, run) => run() }) {
//
if(this.options.taps.length <= 1) {
return this.callTapsSeries({ onError, onResult, onDone, rethrowIfPossible })
}
let code = "";
code += "do {\n";
code += `var _counter = ${this.options.taps.length};\n`;
if(onDone) {
code += "var _done = () => {\n";
code += onDone();
code += "};\n";
}
for(let i = 0; i < this.options.taps.length; i++) {
const done = () => {
if(onDone)
return "if(--_counter === 0) _done();\n";
else
return "--_counter;";
};
const doneBreak = (skipDone) => {
if(skipDone || !onDone)
return "_counter = 0;\n";
else
return "_counter = 0;\n_done();\n";
}
code += "if(_counter <= 0) break;\n";
code += onTap(i, () => this.callTap(i, {
onError: error => {
let code = "";
code += "if(_counter > 0) {\n";
code += onError(i, error, done, doneBreak);
code += "}\n";
return code;
},
onResult: onResult && ((result) => {
let code = "";
code += "if(_counter > 0) {\n";
code += onResult(i, result, done, doneBreak);
code += "}\n";
return code;
}),
onDone: !onResult && (() => {
return done();
}),
rethrowIfPossible
}), done, doneBreak);
}
code += "} while(false);\n";
return code;
}
发现其并行的机制就是通过
js
do {
var _counter = 6;
var _done = () => {
_callback();
};
//
if (_counter <= 0) break;
// 处理拦截器 tap生成执行的伪代码
var _tap0 = _taps[0];
_interceptors[0].tap(_tap0);
// 生成每一个事件流真正的伪代码
var _fn0 = _x[0];
// 执行每一个回调 name, age, done
// 我们异步完成需要手动调用一笑 done() 其也可以传入一个参数(请求失败)
_fn0(xxx, arg2, _err0 => {
// 如果done(有参数传入)
if (_err0) {
// 还有其他的事件流回调还没有执行的话 那么这时候 _counter 还不是0 那么直接进行 整个tapable的回调,且 _counter置为0(这样剩余没有回调的不管失败还是成功都不会执行 _done())
if (_counter > 0) {
_callback(_err0);
_counter = 0;
}
} else {
// 没有一个成功回调 就减一,这样如果减到0说明所有的事件流都回调完成 那么就执行h1.callAsync('立早', 20, () => {})的回调
if (--_counter === 0) _done();
}
});
// 下一个事件流
if (_counter <= 0) break;
var _tap1 = _taps[1];
_interceptors[0].tap(_tap1);
var _fn1 = _x[1];
_fn1(xxx, arg2, _err1 => {
if (_err1) {
if (_counter > 0) {
_callback(_err1);
_counter = 0;
}
} else {
if (--_counter === 0) _done();
}
});
} while (false);
从上面可以看出 并行的方式 其实就是顺序执行所有的事件流的伪代码,但是一般并行会与异步一起,那么其就是通过 一个整体的 _counter 去知道有多少的异步事件流,然后有一个回调完成就 --_counter,并在每一个回调结束中判断 if (--_counter === 0)
_counter 是否为 0,为 0 代表所有的回调都已完成,那么就执行 callAsync() 的回调
全部伪代码
js
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 => {
if (_err0) {
if (_counter > 0) {
_callback(_err0);
_counter = 0;
}
} else {
if (--_counter === 0) _done();
}
});
if (_counter <= 0) break;
var _tap1 = _taps[1];
_interceptors[0].tap(_tap1);
var _fn1 = _x[1];
_fn1(xxx, arg2, _err1 => {
if (_err1) {
if (_counter > 0) {
_callback(_err1);
_counter = 0;
}
} else {
if (--_counter === 0) _done();
}
});
if (_counter <= 0) break;
var _tap2 = _taps[2];
_interceptors[0].tap(_tap2);
var _fn2 = _x[2];
_fn2(xxx, arg2, _err2 => {
if (_err2) {
if (_counter > 0) {
_callback(_err2);
_counter = 0;
}
} else {
if (--_counter === 0) _done();
}
});
if (_counter <= 0) break;
var _tap3 = _taps[3];
_interceptors[0].tap(_tap3);
var _fn3 = _x[3];
_fn3(xxx, arg2, _err3 => {
if (_err3) {
if (_counter > 0) {
_callback(_err3);
_counter = 0;
}
} else {
if (--_counter === 0) _done();
}
});
if (_counter <= 0) break;
var _tap4 = _taps[4];
_interceptors[0].tap(_tap4);
var _fn4 = _x[4];
_fn4(xxx, arg2, _err4 => {
if (_err4) {
if (_counter > 0) {
_callback(_err4);
_counter = 0;
}
} else {
if (--_counter === 0) _done();
}
});
if (_counter <= 0) break;
var _tap5 = _taps[5];
_interceptors[0].tap(_tap5);
var _fn5 = _x[5];
_fn5(xxx, arg2, _err5 => {
if (_err5) {
if (_counter > 0) {
_callback(_err5);
_counter = 0;
}
} else {
if (--_counter === 0) _done();
}
});
} while (false);