Skip to content

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);

上次更新: