Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tapable源码浅析 #95

Open
willson-wang opened this issue Jun 14, 2021 · 0 comments
Open

Tapable源码浅析 #95

willson-wang opened this issue Jun 14, 2021 · 0 comments

Comments

@willson-wang
Copy link
Owner

willson-wang commented Jun 14, 2021

Tabpable 类似node eventemitter的库 控制钩子的函数的发布与订阅,控制webpack的插件系统

Tapable暴露的hook

  • SyncHook 同步钩子

  • SyncBailHook 同步熔断钩子

  • SyncWaterfallHook 同步流水钩子

  • SyncLoopHook 同步循环钩子

  • AsyncParalleHook 异步并发钩子

  • AsyncParalleBailHook 异步并发熔断钩子

  • AsyncSeriesHook 异步串行钩子

  • AsyncSeriesBailHook 异步串行熔断钩子

  • AysncSeriesWaterfallHook 异步串行流水钩子

Hook分类

从功能区分

type function
Waterfall 同步方法,传值下一个函数
Bail 当函数有任何返回值,则在当前执行函数停止
Loop 监听函数返回true则继续循环,返回undefined表示循环结束
Series 串行钩子
Paralle 并行钩子

从类型区分

Async* Sync*
绑定: tapAsync/tapPromise/tap 绑定:tap
执行: callAsync/promise 执行:call

先看个基本例子

const hook = new SyncHook(["arg1", "arg2", "arg3"]);

class Car {
	constructor() {
		this.hooks = {
			accelerate: new SyncHook(["newSpeed"]),
			brake: new SyncHook(),
			calculateRoutes: new AsyncParallelHook(["source", "target", "routesList"])
		};
	}
}

const myCar = new Car();

// 使用tab注册同步钩子的监听函数
myCar.hooks.brake.tap("WarningLampPlugin", () => warningLamp.on());

// tab注册的同步钩子使用call方法进行调用
this.hooks.brake.call();

// 使用tab注册同步钩子的监听函数,与上面不同的是,这里的监听函数接受了一个参数
myCar.hooks.accelerate.tap("LoggerPlugin", newSpeed => console.log(`Accelerating to ${newSpeed}`));

// tab注册的同步钩子使用call方法进行调用,这里传入了一个参数,需要注意的是new SyncHook(["newSpeed"])实例化的时候定义了几个参数,那么在调用钩子时传入的参数就可以接收几个,如果超过则多的参数不会被传递,少的参数会被设置为undefined
this.hooks.accelerate.call(10);

// 异步钩子可以使用tapPromise、tapAsync、tap三种方式监听,触发的时候分别对应promise、callAsync、call
myCar.hooks.calculateRoutes.tapPromise("GoogleMapsPlugin", (source, target, routesList) => {
	// return a promise
	return google.maps.findRoute(source, target).then(route => {
		routesList.add(route);
	});
});
myCar.hooks.calculateRoutes.tapAsync("BingMapsPlugin", (source, target, routesList, callback) => {
	bing.findRoute(source, target, (err, route) => {
		if(err) return callback(err);
		routesList.add(route);
		// call the callback
		callback();
	});
});

myCar.hooks.calculateRoutes.tap("CachedRoutesPlugin", (source, target, routesList) => {
	const cachedRoute = cache.get(source, target);
	if(cachedRoute)
		routesList.add(cachedRoute);
})

然后在跟着例子在使用的时候,比较难理解的一点是异步并行钩子的执行,自己想要了解具体是怎样实现的,所以直接看了下tapable的源码,具体去了解怎么注册监听及执行监听的、具体的钩子是怎么实现的

Tapable 2.2.0 版本

我们先已最简单的hook SyncHook来看

SyncHook.js

class SyncHookCodeFactory extends HookCodeFactory {
    // new Function传入的代码串内this.content的具体实现,确保每个钩子可以定义实现身份钩子的代码
	content({ onError, onDone, rethrowIfPossible }) {
		return this.callTapsSeries({
			onError: (i, err) => onError(err),
			onDone,
			rethrowIfPossible
		});
	}
}

const factory = new SyncHookCodeFactory();

// 最终的call、callAsync、promise函数
const COMPILE = function(options) {
	factory.setup(this, options);
	return factory.create(options);
};

function SyncHook(args = [], name = undefined) {
	const hook = new Hook(args, name);
	hook.constructor = SyncHook;
	hook.tapAsync = TAP_ASYNC;
	hook.tapPromise = TAP_PROMISE;
	hook.compile = COMPILE;
    // new SyncHook 最终返回的是Hook的实例
	return hook;
}
Hook.js 定义基础Hook类,其它的类都继承自这个类,这个类定义了tap、tapPromise、tapAsync注册监听函数的方法,同时又定义了触发监听函数的call、callAsync、promise方法;需要注意的是call、callAsync、promise着三个方法都是调用_createCall方法创建,而_createCall方法内部返回的是this.compile的返回值,实际上每个Hook子类需要自己定义compile方法,而compile方法内又返回的是基类HookCodeFactory上create方法调用的返回值,而基类的create方法调会通过new Function生成一个fn,并返回这个fn,所以我们只要知道这个fn是怎么组织及调用的,我们就可以详细知道每个hook的调用过程,及返回值

const CALL_DELEGATE = function(...args) {
	this.call = this._createCall("sync");
	return this.call(...args);
};
const CALL_ASYNC_DELEGATE = function(...args) {
	this.callAsync = this._createCall("async");
	return this.callAsync(...args);
};
const PROMISE_DELEGATE = function(...args) {
	this.promise = this._createCall("promise");
	return this.promise(...args);
};

class Hook {
	constructor(args = [], name = undefined) {
		this._args = args;
		this.name = name;
		this.taps = [];
		this.interceptors = [];
		this._call = CALL_DELEGATE;
		this.call = CALL_DELEGATE;
		this._callAsync = CALL_ASYNC_DELEGATE;
		this.callAsync = CALL_ASYNC_DELEGATE;
		this._promise = PROMISE_DELEGATE;
		this.promise = PROMISE_DELEGATE;
		this._x = undefined;

		this.compile = this.compile;
		this.tap = this.tap;
		this.tapAsync = this.tapAsync;
		this.tapPromise = this.tapPromise;
	}

	compile(options) {
		throw new Error("Abstract: should be overridden");
	}

	_createCall(type) {
		return this.compile({
			taps: this.taps,
			interceptors: this.interceptors,
			args: this._args,
			type: type
		});
	}

	_tap(type, options, fn) {
        // 格式化tap、tapAsync、tapPromise传入的参数
		if (typeof options === "string") {
			options = {
				name: options.trim()
			};
		} else if (typeof options !== "object" || options === null) {
			throw new Error("Invalid tap options");
		}
		if (typeof options.name !== "string" || options.name === "") {
			throw new Error("Missing name for tap");
		}
        // 注册上下文
		if (typeof options.context !== "undefined") {
			deprecateContext();
		}
		options = Object.assign({ type, fn }, options);
        // 注册拦截器
		options = this._runRegisterInterceptors(options);
		this._insert(options);
	}

	tap(options, fn) {
		this._tap("sync", options, fn);
	}

	tapAsync(options, fn) {
		this._tap("async", options, fn);
	}

	tapPromise(options, fn) {
		this._tap("promise", options, fn);
	}

	_runRegisterInterceptors(options) {
		for (const interceptor of this.interceptors) {
			if (interceptor.register) {
				const newOptions = interceptor.register(options);
				if (newOptions !== undefined) {
					options = newOptions;
				}
			}
		}
		return options;
	}

	_insert(item) {
        // 根据传入的before、stage参数改变tap注册监听函数的属性,正常顺序是按照注册的顺序执行监听函数,同时也支持传入before、state这些参数改变监听函数的执行顺序
		this._resetCompilation();
		let before;
		if (typeof item.before === "string") {
			before = new Set([item.before]);
		} else if (Array.isArray(item.before)) {
			before = new Set(item.before);
		}
		let stage = 0;
		if (typeof item.stage === "number") {
			stage = item.stage;
		}
		let i = this.taps.length;
		while (i > 0) {
			i--;
			const x = this.taps[i];
			this.taps[i + 1] = x;
			const xStage = x.stage || 0;
			if (before) {
				if (before.has(x.name)) {
					before.delete(x.name);
					continue;
				}
				if (before.size > 0) {
					continue;
				}
			}
			if (xStage > stage) {
				continue;
			}
			i++;
			break;
		}
		this.taps[i] = item;
	}
}
HookCodeFactory.js

class HookCodeFactory {
	constructor(config) {
		this.config = config;
		this.options = undefined;
		this._args = undefined;
	}

    // 通过new Function创建最终的call、callAsync、promise函数
    // new Function ([arg1[, arg2[, ...argN]],] functionBody)
	create(options) {
		this.init(options);
		let fn;
		switch (this.options.type) {
			case "sync":
				fn = new Function(
					this.args(),
					'"use strict";\n' +
						this.header() +
						this.contentWithInterceptors({
							onError: err => `throw ${err};\n`,
							onResult: result => `return ${result};\n`,
							resultReturns: true,
							onDone: () => "",
							rethrowIfPossible: true
						})
				);
				break;
			case "async":
				fn = new Function(
					this.args({
						after: "_callback"
					}),
					'"use strict";\n' +
						this.header() +
						this.contentWithInterceptors({
							onError: err => `_callback(${err});\n`,
							onResult: result => `_callback(null, ${result});\n`,
							onDone: () => "_callback();\n"
						})
				);
				break;
			case "promise":
				let errorHelperUsed = false;
				const content = this.contentWithInterceptors({
					onError: err => {
						errorHelperUsed = true;
						return `_error(${err});\n`;
					},
					onResult: result => `_resolve(${result});\n`,
					onDone: () => "_resolve();\n"
				});
				let code = "";
				code += '"use strict";\n';
				code += this.header();
				code += "return new Promise((function(_resolve, _reject) {\n";
				if (errorHelperUsed) {
					code += "var _sync = true;\n";
					code += "function _error(_err) {\n";
					code += "if(_sync)\n";
					code +=
						"_resolve(Promise.resolve().then((function() { throw _err; })));\n";
					code += "else\n";
					code += "_reject(_err);\n";
					code += "};\n";
				}
				code += content;
				if (errorHelperUsed) {
					code += "_sync = false;\n";
				}
				code += "}));\n";
				fn = new Function(this.args(), code);
				break;
		}
		this.deinit();
		return fn;
	}

	setup(instance, options) {
		instance._x = options.taps.map(t => t.fn);
	}

	init(options) {
		this.options = options;
		this._args = options.args.slice();
	}

	deinit() {
		this.options = undefined;
		this._args = undefined;
	}

	contentWithInterceptors(options) {
		if (this.options.interceptors.length > 0) {
			const onError = options.onError;
			const onResult = options.onResult;
			const onDone = options.onDone;
			let code = "";
			for (let i = 0; i < this.options.interceptors.length; i++) {
				const interceptor = this.options.interceptors[i];
				if (interceptor.call) {
					code += `${this.getInterceptor(i)}.call(${this.args({
						before: interceptor.context ? "_context" : undefined
					})});\n`;
				}
			}
			code += this.content(
				Object.assign(options, {
					onError:
						onError &&
						(err => {
							let code = "";
							for (let i = 0; i < this.options.interceptors.length; i++) {
								const interceptor = this.options.interceptors[i];
								if (interceptor.error) {
									code += `${this.getInterceptor(i)}.error(${err});\n`;
								}
							}
							code += onError(err);
							return code;
						}),
					onResult:
						onResult &&
						(result => {
							let code = "";
							for (let i = 0; i < this.options.interceptors.length; i++) {
								const interceptor = this.options.interceptors[i];
								if (interceptor.result) {
									code += `${this.getInterceptor(i)}.result(${result});\n`;
								}
							}
							code += onResult(result);
							return code;
						}),
					onDone:
						onDone &&
						(() => {
							let code = "";
							for (let i = 0; i < this.options.interceptors.length; i++) {
								const interceptor = this.options.interceptors[i];
								if (interceptor.done) {
									code += `${this.getInterceptor(i)}.done();\n`;
								}
							}
							code += onDone();
							return code;
						})
				})
			);
			return code;
		} else {
			return this.content(options);
		}
	}

	header() {
		let code = "";
		if (this.needContext()) {
			code += "var _context = {};\n";
		} else {
			code += "var _context;\n";
		}
		code += "var _x = this._x;\n";
		if (this.options.interceptors.length > 0) {
			code += "var _taps = this.taps;\n";
			code += "var _interceptors = this.interceptors;\n";
		}
		return code;
	}

	needContext() {
		for (const tap of this.options.taps) if (tap.context) return true;
		return false;
	}

	callTap(tapIndex, { onError, onResult, onDone, rethrowIfPossible }) {
		let code = "";
		let hasTapCached = false;
		for (let i = 0; i < this.options.interceptors.length; i++) {
			const interceptor = this.options.interceptors[i];
			if (interceptor.tap) {
				if (!hasTapCached) {
					code += `var _tap${tapIndex} = ${this.getTap(tapIndex)};\n`;
					hasTapCached = true;
				}
				code += `${this.getInterceptor(i)}.tap(${
					interceptor.context ? "_context, " : ""
				}_tap${tapIndex});\n`;
			}
		}
		code += `var _fn${tapIndex} = ${this.getTapFn(tapIndex)};\n`;
		const tap = this.options.taps[tapIndex];
		switch (tap.type) {
			case "sync":
				if (!rethrowIfPossible) {
					code += `var _hasError${tapIndex} = false;\n`;
					code += "try {\n";
				}
				if (onResult) {
					code += `var _result${tapIndex} = _fn${tapIndex}(${this.args({
						before: tap.context ? "_context" : undefined
					})});\n`;
				} else {
					code += `_fn${tapIndex}(${this.args({
						before: tap.context ? "_context" : undefined
					})});\n`;
				}
				if (!rethrowIfPossible) {
					code += "} catch(_err) {\n";
					code += `_hasError${tapIndex} = true;\n`;
					code += onError("_err");
					code += "}\n";
					code += `if(!_hasError${tapIndex}) {\n`;
				}
				if (onResult) {
					code += onResult(`_result${tapIndex}`);
				}
				if (onDone) {
					code += onDone();
				}
				if (!rethrowIfPossible) {
					code += "}\n";
				}
				break;
			case "async":
				let cbCode = "";
				if (onResult)
					cbCode += `(function(_err${tapIndex}, _result${tapIndex}) {\n`;
				else cbCode += `(function(_err${tapIndex}) {\n`;
				cbCode += `if(_err${tapIndex}) {\n`;
				cbCode += onError(`_err${tapIndex}`);
				cbCode += "} else {\n";
				if (onResult) {
					cbCode += onResult(`_result${tapIndex}`);
				}
				if (onDone) {
					cbCode += onDone();
				}
				cbCode += "}\n";
				cbCode += "})";
				code += `_fn${tapIndex}(${this.args({
					before: tap.context ? "_context" : undefined,
					after: cbCode
				})});\n`;
				break;
			case "promise":
				code += `var _hasResult${tapIndex} = false;\n`;
				code += `var _promise${tapIndex} = _fn${tapIndex}(${this.args({
					before: tap.context ? "_context" : undefined
				})});\n`;
				code += `if (!_promise${tapIndex} || !_promise${tapIndex}.then)\n`;
				code += `  throw new Error('Tap function (tapPromise) did not return promise (returned ' + _promise${tapIndex} + ')');\n`;
				code += `_promise${tapIndex}.then((function(_result${tapIndex}) {\n`;
				code += `_hasResult${tapIndex} = true;\n`;
				if (onResult) {
					code += onResult(`_result${tapIndex}`);
				}
				if (onDone) {
					code += onDone();
				}
				code += `}), function(_err${tapIndex}) {\n`;
				code += `if(_hasResult${tapIndex}) throw _err${tapIndex};\n`;
				code += onError(`_err${tapIndex}`);
				code += "});\n";
				break;
		}
		return code;
	}

	callTapsSeries({
		onError,
		onResult,
		resultReturns,
		onDone,
		doneReturns,
		rethrowIfPossible
	}) {
		if (this.options.taps.length === 0) return onDone();
		const firstAsync = this.options.taps.findIndex(t => t.type !== "sync");
		const somethingReturns = resultReturns || doneReturns;
		let code = "";
		let current = onDone;
		let unrollCounter = 0;
		for (let j = this.options.taps.length - 1; j >= 0; j--) {
			const i = j;
			const unroll =
				current !== onDone &&
				(this.options.taps[i].type !== "sync" || unrollCounter++ > 20);
			if (unroll) {
				unrollCounter = 0;
				code += `function _next${i}() {\n`;
				code += current();
				code += `}\n`;
				current = () => `${somethingReturns ? "return " : ""}_next${i}();\n`;
			}
			const done = current;
			const doneBreak = skipDone => {
				if (skipDone) return "";
				return onDone();
			};
			const content = this.callTap(i, {
				onError: error => onError(i, error, done, doneBreak),
				onResult:
					onResult &&
					(result => {
						return onResult(i, result, done, doneBreak);
					}),
				onDone: !onResult && done,
				rethrowIfPossible:
					rethrowIfPossible && (firstAsync < 0 || i < firstAsync)
			});
			current = () => content;
		}
		code += current();
		return code;
	}

	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 = (function() {\n";
			code += "var _loopAsync = false;\n";
		}
		code += "var _loop;\n";
		code += "do {\n";
		code += "_loop = false;\n";
		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;
	}

	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 = (function() {\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;
	}

	args({ before, after } = {}) {
		let allArgs = this._args;
		if (before) allArgs = [before].concat(allArgs);
		if (after) allArgs = allArgs.concat(after);
		if (allArgs.length === 0) {
			return "";
		} else {
			return allArgs.join(", ");
		}
	}

	getTapFn(idx) {
		return `_x[${idx}]`;
	}

	getTap(idx) {
		return `_taps[${idx}]`;
	}

	getInterceptor(idx) {
		return `_interceptors[${idx}]`;
	}
}

我们用一个例子与源码来看下,最终输出的

class Car {
    constructor() {
        this.hooks = {
            // new SyncHook 传入的参数,就是定义call的时候需要传几个参数,然后tap的时候可以接受call传入的几个参数,所以要想tap的时候能过接收到参数,call的时候传入的参数有效,则实例化的时候需要传入参数
            syncHook: new SyncHook(["SyncHookarg1", "SyncHookarg2", "SyncHookarg3"]),
            syncBailHook: new SyncBailHook(["syncBailHookarg1", "syncBailHookarg2", "syncBailHookarg3"]),
            syncWaterfallHook: new SyncWaterfallHook(["syncWaterfallHookarg1", "syncWaterfallHookarg2", "syncWaterfallHookarg3"]),
            syncLoopHook: new SyncLoopHook(["syncLoopHookarg1", "syncLoopHookarg2", "syncLoopHookarg3"]),
            asyncParallelHook: new AsyncParallelHook(["asyncParallelHookarg1", "asyncParallelHookarg2", "asyncParallelHookarg3"]),
            asyncParallelBailHook: new AsyncParallelBailHook(["asyncParallelBailHookarg1", "asyncParallelBailHookarg2", "asyncParallelBailHookarg3"]),
            asyncSeriesHook: new AsyncSeriesHook(["asyncSeriesHookarg1", "asyncSeriesHookarg2", "asyncSeriesHookarg3"]),
            asyncSeriesBailHook: new AsyncSeriesBailHook(["asyncSeriesBailHookarg1", "asyncSeriesBailHookarg2", "asyncSeriesBailHookarg3"]),
            asyncSeriesWaterfallHook: new AsyncSeriesWaterfallHook(["asyncSeriesWaterfallHookarg1", "asyncSeriesWaterfallHookarg2", "asyncSeriesWaterfallHookarg3"]),
        }
    }
}

// 同步钩子例子,每次需要更改对应的属性key即可测试验证
car.hooks.syncHook.tap('start', (...args) => {
    console.log('start', ...args)
    // return 没有意义
    return undefined
})

car.hooks.syncHook.tap('speed', (...args) => {
    // throw new Error('speed')
    let i = 0
    while(i < 2000000000) {
        i += 1
    }
    console.log('speed', ...args)
    return undefined
})

car.hooks.syncHook.tap('stop', (...args) => {
    console.log('stop', ...args)
    return undefined
})

car.hooks.syncHook.tap('end', (...args) => {
    console.log('end', ...args)
    return undefined
})

const syncResult = car.hooks.syncHook.call(1, 2, 3)


// 异步 tapAsync注册的钩子
car.hooks.asyncParallelBailHook.tapAsync('start', (...args) => {
    console.log('start', ...(args.slice(0, 3)))
    const callback = args[args.length - 1]
    return callback()
})

car.hooks.asyncParallelBailHook.tapAsync('speed', (...args) => {
    let i = 0
    while(i < 2000000000) {
        i += 1
    }
    console.log('speed', ...(args.slice(0, 3)))
    const callback = args[args.length - 1]
    return callback(null, '77')
})

car.hooks.asyncParallelBailHook.tapAsync('stop', (...args) => {
    console.log('stop', ...(args.slice(0, 3)))
    const callback = args[args.length - 1]
    return callback(new Error('stop'), '66')
})

car.hooks.asyncParallelBailHook.tapAsync('end', (...args) => {
    console.log('end', ...(args.slice(0, 3)))
    const callback = args[args.length - 1]
    return callback(null, '55')
})

car.hooks.asyncParallelBailHook.callAsync(1, 2, 3, (error, result) => {
    console.log('async result', error, result)
})


// 异步 tapPromise注册的钩子
car.hooks.asyncParallelHook.tapPromise('start', (...args) => {
    console.log('start', ...(args.slice(0, 3)))
    return new Promise((resolve) => {
        setTimeout(() => {
            resolve(undefined)
        }, 5000)
    })
})

car.hooks.asyncParallelHook.tapPromise('speed', (...args) => {
    let i = 0
    while(i < 2000000000) {
        i += 1
    }
    console.log('speed', ...(args.slice(0, 3)))
    // return callback('77')
    return Promise.resolve(77)
})

car.hooks.asyncParallelHook.tapPromise('stop', (...args) => {
    console.log('stop', ...(args.slice(0, 3)))
    // return callback('66')
    return Promise.reject(new Error('stop'))
})

car.hooks.asyncParallelHook.tapPromise('end', (...args) => {
    console.log('end', ...(args.slice(0, 3)))
    // return callback('55')
    return Promise.resolve(55)
})

car.hooks.asyncParallelHook.promise(1, 2, 3).then((result) => {
    console.log('async promise result', result)
    return result
}).catch((err) => {
    console.log('async promise err', err.message)
})

syncHook

输出new Function中传入的BodyCode部分

"use strict";
var _context;
var _x = this._x;
var _fn0 = _x[0];
_fn0(SyncHookarg1, SyncHookarg2, SyncHookarg3);
var _fn1 = _x[1];
_fn1(SyncHookarg1, SyncHookarg2, SyncHookarg3);
var _fn2 = _x[2];
_fn2(SyncHookarg1, SyncHookarg2, SyncHookarg3);
var _fn3 = _x[3];
_fn3(SyncHookarg1, SyncHookarg2, SyncHookarg3);

从上面最终生成的BodyCode来看,按照taps顺序执行,其中一个出错,后续会中断执行,最终call的返回值为undefined

syncBailHook

"use strict";
var _context;
var _x = this._x;
var _fn0 = _x[0];
var _result0 = _fn0(syncBailHookarg1, syncBailHookarg2, syncBailHookarg3);
if (_result0 !== undefined) {
    return _result0;;
} else {
    var _fn1 = _x[1];
    var _result1 = _fn1(syncBailHookarg1, syncBailHookarg2, syncBailHookarg3);
    if (_result1 !== undefined) {
        return _result1;;
    } else {
        var _fn2 = _x[2];
        var _result2 = _fn2(syncBailHookarg1, syncBailHookarg2, syncBailHookarg3);
        if (_result2 !== undefined) {
            return _result2;;
        } else {
            var _fn3 = _x[3];
            var _result3 = _fn3(syncBailHookarg1, syncBailHookarg2, syncBailHookarg3);
            if (_result3 !== undefined) {
                return _result3;;
            } else {}
        }
    }
}

从上面最终生成的BodyCode来看,按照taps顺序执行,其中一个出错,后续会中断执行,当其中一个监听函数有返回值(返回值不能是undefined 或者 undefined字符串),则后续的监听函数不会在执行,call的返回值为return的值

syncWaterfallHook

"use strict";
var _context;
var _x = this._x;
var _fn0 = _x[0];
var _result0 = _fn0(syncWaterfallHookarg1, syncWaterfallHookarg2, syncWaterfallHookarg3);
if (_result0 !== undefined) {
    syncWaterfallHookarg1 = _result0;
}
var _fn1 = _x[1];
var _result1 = _fn1(syncWaterfallHookarg1, syncWaterfallHookarg2, syncWaterfallHookarg3);
if (_result1 !== undefined) {
    syncWaterfallHookarg1 = _result1;
}
var _fn2 = _x[2];
var _result2 = _fn2(syncWaterfallHookarg1, syncWaterfallHookarg2, syncWaterfallHookarg3);
if (_result2 !== undefined) {
    syncWaterfallHookarg1 = _result2;
}
var _fn3 = _x[3];
var _result3 = _fn3(syncWaterfallHookarg1, syncWaterfallHookarg2, syncWaterfallHookarg3);
if (_result3 !== undefined) {
    syncWaterfallHookarg1 = _result3;
}
return syncWaterfallHookarg1;

从上面最终生成的BodyCode来看,按照taps顺序执行,其中一个出错,后续会中断执行,前一个监听函数返回的值不为undefined则会被当成第一个参数传入到下一个监听函数,call最后的返回值是最后一个监听函数的return的值

syncLoopHook

"use strict";
var _context;
var _x = this._x;
var _loop;
do {
    _loop = false;
    var _fn0 = _x[0];
    var _result0 = _fn0(syncLoopHookarg1, syncLoopHookarg2, syncLoopHookarg3);
    if (_result0 !== undefined) {
        _loop = true;
    } else {
        var _fn1 = _x[1];
        var _result1 = _fn1(syncLoopHookarg1, syncLoopHookarg2, syncLoopHookarg3);
        if (_result1 !== undefined) {
            _loop = true;
        } else {
            var _fn2 = _x[2];
            var _result2 = _fn2(syncLoopHookarg1, syncLoopHookarg2, syncLoopHookarg3);
            if (_result2 !== undefined) {
                _loop = true;
            } else {
                var _fn3 = _x[3];
                var _result3 = _fn3(syncLoopHookarg1, syncLoopHookarg2, syncLoopHookarg3);
                if (_result3 !== undefined) {
                    _loop = true;
                } else {
                    if (!_loop) {}
                }
            }
        }
    }
} while (_loop);

从上面最终生成的BodyCode来看,按照taps顺序执行,其中一个出错,后续会中断执行,按顺序执行的过程中,监听函数没有返回undefined,则会按照顺序重新执行,直到所有的监听函数都返回undefined,call最终的返回值为undefiend

asyncSeriesHook tapPromise promise

"use strict";
var _context;
var _x = this._x;
return new Promise((function (_resolve, _reject) {
    var _sync = true;

    function _error(_err) {
        if (_sync)
            _resolve(Promise.resolve().then((function () {
                throw _err;
            })));
        else
            _reject(_err);
    };

    function _next2() {
        var _fn3 = _x[3];
        var _hasResult3 = false;
        var _promise3 = _fn3(asyncSeriesHookarg1, asyncSeriesHookarg2, asyncSeriesHookarg3);
        if (!_promise3 || !_promise3.then)
            throw new Error('Tap function (tapPromise) did not return promise (returned ' + _promise3 + ')');
        _promise3.then((function (_result3) {
            _hasResult3 = true;
            _resolve();
        }), function (_err3) {
            if (_hasResult3) throw _err3;
            _error(_err3);
        });
    }

    function _next1() {
        var _fn2 = _x[2];
        var _hasResult2 = false;
        var _promise2 = _fn2(asyncSeriesHookarg1, asyncSeriesHookarg2, asyncSeriesHookarg3);
        if (!_promise2 || !_promise2.then)
            throw new Error('Tap function (tapPromise) did not return promise (returned ' + _promise2 + ')');
        _promise2.then((function (_result2) {
            _hasResult2 = true;
            _next2();
        }), function (_err2) {
            if (_hasResult2) throw _err2;
            _error(_err2);
        });
    }

    function _next0() {
        var _fn1 = _x[1];
        var _hasResult1 = false;
        var _promise1 = _fn1(asyncSeriesHookarg1, asyncSeriesHookarg2, asyncSeriesHookarg3);
        if (!_promise1 || !_promise1.then)
            throw new Error('Tap function (tapPromise) did not return promise (returned ' + _promise1 + ')');
        _promise1.then((function (_result1) {
            _hasResult1 = true;
            _next1();
        }), function (_err1) {
            if (_hasResult1) throw _err1;
            _error(_err1);
        });
    }
    var _fn0 = _x[0];
    var _hasResult0 = false;
    var _promise0 = _fn0(asyncSeriesHookarg1, asyncSeriesHookarg2, asyncSeriesHookarg3);
    if (!_promise0 || !_promise0.then)
        throw new Error('Tap function (tapPromise) did not return promise (returned ' + _promise0 + ')');
    _promise0.then((function (_result0) {
        _hasResult0 = true;
        _next0();
    }), function (_err0) {
        if (_hasResult0) throw _err0;
        _error(_err0);
    });
    _sync = false;
}));

从上面最终生成的BodyCode来看,没有最终返回值,按照顺序执行,只要其中一个抛出错误,中断后续执行,并进入catch捕获函数

asyncSeriesHook tapAsync callAsync

"use strict";
var _context;
var _x = this._x;

function _next2() {
    var _fn3 = _x[3];
    _fn3(asyncSeriesHookarg1, asyncSeriesHookarg2, asyncSeriesHookarg3, (function (_err3) {
        if (_err3) {
            _callback(_err3);
        } else {
            _callback();
        }
    }));
}

function _next1() {
    var _fn2 = _x[2];
    _fn2(asyncSeriesHookarg1, asyncSeriesHookarg2, asyncSeriesHookarg3, (function (_err2) {
        if (_err2) {
            _callback(_err2);
        } else {
            _next2();
        }
    }));
}

function _next0() {
    var _fn1 = _x[1];
    _fn1(asyncSeriesHookarg1, asyncSeriesHookarg2, asyncSeriesHookarg3, (function (_err1) {
        if (_err1) {
            _callback(_err1);
        } else {
            _next1();
        }
    }));
}
var _fn0 = _x[0];
_fn0(asyncSeriesHookarg1, asyncSeriesHookarg2, asyncSeriesHookarg3, (function (_err0) {
    if (_err0) {
        _callback(_err0);
    } else {
        _next0();
    }
}));

从上面最终生成的BodyCode来看, 没有最终返回值,按照taps顺序执行,只要其中一个抛出错误,中断后续执行,并执行callAsync传入的callback函数,第一个参数为接受到的错误,与promise区别的是,每个tapAsync传入的最后一个参数是一个next函数,改函数接受一个错误参数,如果传入该参数则直接调用callAsync的回调callback

对于asyncSeriesHook钩子 tapAsync、tapPromise效果一致

asyncSeriesBailHook tapPromise promise

"use strict";
var _context;
var _x = this._x;
return new Promise((function (_resolve, _reject) {
    var _sync = true;

    function _error(_err) {
        if (_sync)
            _resolve(Promise.resolve().then((function () {
                throw _err;
            })));
        else
            _reject(_err);
    };

    function _next2() {
        var _fn3 = _x[3];
        var _hasResult3 = false;
        var _promise3 = _fn3(asyncSeriesBailHookarg1, asyncSeriesBailHookarg2, asyncSeriesBailHookarg3);
        if (!_promise3 || !_promise3.then)
            throw new Error('Tap function (tapPromise) did not return promise (returned ' + _promise3 + ')');
        _promise3.then((function (_result3) {
            _hasResult3 = true;
            if (_result3 !== undefined) {
                _resolve(_result3);

            } else {
                _resolve();
            }
        }), function (_err3) {
            if (_hasResult3) throw _err3;
            _error(_err3);
        });
    }

    function _next1() {
        var _fn2 = _x[2];
        var _hasResult2 = false;
        var _promise2 = _fn2(asyncSeriesBailHookarg1, asyncSeriesBailHookarg2, asyncSeriesBailHookarg3);
        if (!_promise2 || !_promise2.then)
            throw new Error('Tap function (tapPromise) did not return promise (returned ' + _promise2 + ')');
        _promise2.then((function (_result2) {
            _hasResult2 = true;
            if (_result2 !== undefined) {
                _resolve(_result2);

            } else {
                _next2();
            }
        }), function (_err2) {
            if (_hasResult2) throw _err2;
            _error(_err2);
        });
    }

    function _next0() {
        var _fn1 = _x[1];
        var _hasResult1 = false;
        var _promise1 = _fn1(asyncSeriesBailHookarg1, asyncSeriesBailHookarg2, asyncSeriesBailHookarg3);
        if (!_promise1 || !_promise1.then)
            throw new Error('Tap function (tapPromise) did not return promise (returned ' + _promise1 + ')');
        _promise1.then((function (_result1) {
            _hasResult1 = true;
            if (_result1 !== undefined) {
                _resolve(_result1);

            } else {
                _next1();
            }
        }), function (_err1) {
            if (_hasResult1) throw _err1;
            _error(_err1);
        });
    }
    var _fn0 = _x[0];
    var _hasResult0 = false;
    var _promise0 = _fn0(asyncSeriesBailHookarg1, asyncSeriesBailHookarg2, asyncSeriesBailHookarg3);
    if (!_promise0 || !_promise0.then)
        throw new Error('Tap function (tapPromise) did not return promise (returned ' + _promise0 + ')');
    _promise0.then((function (_result0) {
        _hasResult0 = true;
        if (_result0 !== undefined) {
            _resolve(_result0);

        } else {
            _next0();
        }
    }), function (_err0) {
        if (_hasResult0) throw _err0;
        _error(_err0);
    });
    _sync = false;
}));

从上面最终生成的BodyCode来看, 按照taps顺序执行,如果监听函数有返回值,则直接resolve,后续的监听函数不在执行,如果抛出错误与asyncSeriesHook钩子一样,通过catch函数捕获

asyncSeriesBailHook tapAsync callAsync

"use strict";
var _context;
var _x = this._x;

function _next2() {
    var _fn3 = _x[3];
    _fn3(asyncSeriesBailHookarg1, asyncSeriesBailHookarg2, asyncSeriesBailHookarg3, (function (_err3, _result3) {
        if (_err3) {
            _callback(_err3);
        } else {
            if (_result3 !== undefined) {
                _callback(null, _result3);

            } else {
                _callback();
            }
        }
    }));
}

function _next1() {
    var _fn2 = _x[2];
    _fn2(asyncSeriesBailHookarg1, asyncSeriesBailHookarg2, asyncSeriesBailHookarg3, (function (_err2, _result2) {
        if (_err2) {
            _callback(_err2);
        } else {
            if (_result2 !== undefined) {
                _callback(null, _result2);

            } else {
                _next2();
            }
        }
    }));
}

function _next0() {
    var _fn1 = _x[1];
    _fn1(asyncSeriesBailHookarg1, asyncSeriesBailHookarg2, asyncSeriesBailHookarg3, (function (_err1, _result1) {
        if (_err1) {
            _callback(_err1);
        } else {
            if (_result1 !== undefined) {
                _callback(null, _result1);

            } else {
                _next1();
            }
        }
    }));
}
var _fn0 = _x[0];
_fn0(asyncSeriesBailHookarg1, asyncSeriesBailHookarg2, asyncSeriesBailHookarg3, (function (_err0, _result0) {
    if (_err0) {
        _callback(_err0);
    } else {
        if (_result0 !== undefined) {
            _callback(null, _result0);

        } else {
            _next0();
        }
    }
}));

从上面最终生成的BodyCode来看, 按照taps顺序执行,如果监听函数有返回值,则直接调用callAsync传入的callback,并将返回的结果传入第二个参数,后续的监听函数不在执行,如果抛出错误则直接调用callAsync传入的callback并将错误信息传入第一个参数

asyncSeriesBailHook tapPromise 与 tapAsync 效果一致

asyncSeriesWaterfallHook tapPromise promise

"use strict";
var _context;
var _x = this._x;
return new Promise((function (_resolve, _reject) {
    var _sync = true;

    function _error(_err) {
        if (_sync)
            _resolve(Promise.resolve().then((function () {
                throw _err;
            })));
        else
            _reject(_err);
    };

    function _next2() {
        var _fn3 = _x[3];
        var _hasResult3 = false;
        var _promise3 = _fn3(asyncSeriesWaterfallHookarg1, asyncSeriesWaterfallHookarg2, asyncSeriesWaterfallHookarg3);
        if (!_promise3 || !_promise3.then)
            throw new Error('Tap function (tapPromise) did not return promise (returned ' + _promise3 + ')');
        _promise3.then((function (_result3) {
            _hasResult3 = true;
            if (_result3 !== undefined) {
                asyncSeriesWaterfallHookarg1 = _result3;
            }
            _resolve(asyncSeriesWaterfallHookarg1);
        }), function (_err3) {
            if (_hasResult3) throw _err3;
            _error(_err3);
        });
    }

    function _next1() {
        var _fn2 = _x[2];
        var _hasResult2 = false;
        var _promise2 = _fn2(asyncSeriesWaterfallHookarg1, asyncSeriesWaterfallHookarg2, asyncSeriesWaterfallHookarg3);
        if (!_promise2 || !_promise2.then)
            throw new Error('Tap function (tapPromise) did not return promise (returned ' + _promise2 + ')');
        _promise2.then((function (_result2) {
            _hasResult2 = true;
            if (_result2 !== undefined) {
                asyncSeriesWaterfallHookarg1 = _result2;
            }
            _next2();
        }), function (_err2) {
            if (_hasResult2) throw _err2;
            _error(_err2);
        });
    }

    function _next0() {
        var _fn1 = _x[1];
        var _hasResult1 = false;
        var _promise1 = _fn1(asyncSeriesWaterfallHookarg1, asyncSeriesWaterfallHookarg2, asyncSeriesWaterfallHookarg3);
        if (!_promise1 || !_promise1.then)
            throw new Error('Tap function (tapPromise) did not return promise (returned ' + _promise1 + ')');
        _promise1.then((function (_result1) {
            _hasResult1 = true;
            if (_result1 !== undefined) {
                asyncSeriesWaterfallHookarg1 = _result1;
            }
            _next1();
        }), function (_err1) {
            if (_hasResult1) throw _err1;
            _error(_err1);
        });
    }
    var _fn0 = _x[0];
    var _hasResult0 = false;
    var _promise0 = _fn0(asyncSeriesWaterfallHookarg1, asyncSeriesWaterfallHookarg2, asyncSeriesWaterfallHookarg3);
    if (!_promise0 || !_promise0.then)
        throw new Error('Tap function (tapPromise) did not return promise (returned ' + _promise0 + ')');
    _promise0.then((function (_result0) {
        _hasResult0 = true;
        if (_result0 !== undefined) {
            asyncSeriesWaterfallHookarg1 = _result0;
        }
        _next0();
    }), function (_err0) {
        if (_hasResult0) throw _err0;
        _error(_err0);
    });
    _sync = false;
}));

从上面最终生成的BodyCode来看, 按照taps顺序执行,如果监听函数有返回值,则直接resolve,并将resolve的值替换第一个参数并传入到下一个监听函数,最终resolve出去的值为,最后一个监听函数resolve的值,如果抛出错误则中断执行,通过catch函数捕获

asyncSeriesWaterfallHook tapAsync callAsync

"use strict";
var _context;
var _x = this._x;

function _next2() {
    var _fn3 = _x[3];
    _fn3(asyncSeriesWaterfallHookarg1, asyncSeriesWaterfallHookarg2, asyncSeriesWaterfallHookarg3, (function (_err3, _result3) {
        if (_err3) {
            _callback(_err3);
        } else {
            if (_result3 !== undefined) {
                asyncSeriesWaterfallHookarg1 = _result3;
            }
            _callback(null, asyncSeriesWaterfallHookarg1);
        }
    }));
}

function _next1() {
    var _fn2 = _x[2];
    _fn2(asyncSeriesWaterfallHookarg1, asyncSeriesWaterfallHookarg2, asyncSeriesWaterfallHookarg3, (function (_err2, _result2) {
        if (_err2) {
            _callback(_err2);
        } else {
            if (_result2 !== undefined) {
                asyncSeriesWaterfallHookarg1 = _result2;
            }
            _next2();
        }
    }));
}

function _next0() {
    var _fn1 = _x[1];
    _fn1(asyncSeriesWaterfallHookarg1, asyncSeriesWaterfallHookarg2, asyncSeriesWaterfallHookarg3, (function (_err1, _result1) {
        if (_err1) {
            _callback(_err1);
        } else {
            if (_result1 !== undefined) {
                asyncSeriesWaterfallHookarg1 = _result1;
            }
            _next1();
        }
    }));
}
var _fn0 = _x[0];
_fn0(asyncSeriesWaterfallHookarg1, asyncSeriesWaterfallHookarg2, asyncSeriesWaterfallHookarg3, (function (_err0, _result0) {
    if (_err0) {
        _callback(_err0);
    } else {
        if (_result0 !== undefined) {
            asyncSeriesWaterfallHookarg1 = _result0;
        }
        _next0();
    }
}));

从上面最终生成的BodyCode来看, 按照taps顺序执行,如果监听函数有返回值,则调用next,并return的值替换第一个参数并传入到下一个监听函数,最终return出去的值为,最后一个监听函数return的值,如果抛出错误则中断执行,并在callAsync的callback内接受到第一个参数,值为错误信息

asyncSeriesWaterfallHook tapPromise 与 tapAsync的效果一致

asyncParallelHook tapPromise promise

"use strict";
var _context;
var _x = this._x;
return new Promise((function (_resolve, _reject) {
    var _sync = true;

    function _error(_err) {
        if (_sync)
            _resolve(Promise.resolve().then((function () {
                throw _err;
            })));
        else
            _reject(_err);
    };
    do {
        var _counter = 4;
        var _done = (function () {
            _resolve();
        });
        if (_counter <= 0) break;
        var _fn0 = _x[0];
        var _hasResult0 = false;
        var _promise0 = _fn0(asyncParallelHookarg1, asyncParallelHookarg2, asyncParallelHookarg3);
        if (!_promise0 || !_promise0.then)
            throw new Error('Tap function (tapPromise) did not return promise (returned ' + _promise0 + ')');
        _promise0.then((function (_result0) {
            _hasResult0 = true;
            if (--_counter === 0) _done();
        }), function (_err0) {
            if (_hasResult0) throw _err0;
            if (_counter > 0) {
                _error(_err0);
                _counter = 0;
            }
        });
        if (_counter <= 0) break;
        var _fn1 = _x[1];
        var _hasResult1 = false;
        var _promise1 = _fn1(asyncParallelHookarg1, asyncParallelHookarg2, asyncParallelHookarg3);
        if (!_promise1 || !_promise1.then)
            throw new Error('Tap function (tapPromise) did not return promise (returned ' + _promise1 + ')');
        _promise1.then((function (_result1) {
            _hasResult1 = true;
            if (--_counter === 0) _done();
        }), function (_err1) {
            if (_hasResult1) throw _err1;
            if (_counter > 0) {
                _error(_err1);
                _counter = 0;
            }
        });
        if (_counter <= 0) break;
        var _fn2 = _x[2];
        var _hasResult2 = false;
        var _promise2 = _fn2(asyncParallelHookarg1, asyncParallelHookarg2, asyncParallelHookarg3);
        if (!_promise2 || !_promise2.then)
            throw new Error('Tap function (tapPromise) did not return promise (returned ' + _promise2 + ')');
        _promise2.then((function (_result2) {
            _hasResult2 = true;
            if (--_counter === 0) _done();
        }), function (_err2) {
            if (_hasResult2) throw _err2;
            if (_counter > 0) {
                _error(_err2);
                _counter = 0;
            }
        });
        if (_counter <= 0) break;
        var _fn3 = _x[3];
        var _hasResult3 = false;
        var _promise3 = _fn3(asyncParallelHookarg1, asyncParallelHookarg2, asyncParallelHookarg3);
        if (!_promise3 || !_promise3.then)
            throw new Error('Tap function (tapPromise) did not return promise (returned ' + _promise3 + ')');
        _promise3.then((function (_result3) {
            _hasResult3 = true;
            if (--_counter === 0) _done();
        }), function (_err3) {
            if (_hasResult3) throw _err3;
            if (_counter > 0) {
                _error(_err3);
                _counter = 0;
            }
        });
    } while (false);
    _sync = false;
}));

从上面最终生成的BodyCode来看, 按照taps生成对应的promise,每个promise都会执行,当任何一个promise先抛出错误,则会导致最终执行的promise()进入catch

asyncParallel tapAsync callAsync

"use strict";
var _context;
var _x = this._x;
do {
    var _counter = 4;
    var _done = (function () {
        _callback();
    });
    if (_counter <= 0) break;
    var _fn0 = _x[0];
    _fn0(asyncParallelHookarg1, asyncParallelHookarg2, asyncParallelHookarg3, (function (_err0) {
        if (_err0) {
            if (_counter > 0) {
                _callback(_err0);
                _counter = 0;
            }
        } else {
            if (--_counter === 0) _done();
        }
    }));
    if (_counter <= 0) break;
    var _fn1 = _x[1];
    _fn1(asyncParallelHookarg1, asyncParallelHookarg2, asyncParallelHookarg3, (function (_err1) {
        if (_err1) {
            if (_counter > 0) {
                _callback(_err1);
                _counter = 0;
            }
        } else {
            if (--_counter === 0) _done();
        }
    }));
    if (_counter <= 0) break;
    var _fn2 = _x[2];
    _fn2(asyncParallelHookarg1, asyncParallelHookarg2, asyncParallelHookarg3, (function (_err2) {
        if (_err2) {
            if (_counter > 0) {
                _callback(_err2);
                _counter = 0;
            }
        } else {
            if (--_counter === 0) _done();
        }
    }));
    if (_counter <= 0) break;
    var _fn3 = _x[3];
    _fn3(asyncParallelHookarg1, asyncParallelHookarg2, asyncParallelHookarg3, (function (_err3) {
        if (_err3) {
            if (_counter > 0) {
                _callback(_err3);
                _counter = 0;
            }
        } else {
            if (--_counter === 0) _done();
        }
    }));
} while (false)

从上面最终生成的BodyCode来看, 按照taps调用对应的监听函数,当任何一个监听函数先抛出错误,则会导致后面的监听函数不会执行,且会触发callAsync传入的callback,并将错误参数当成第一个参数传入;注意这里与tapPromise注册方式有区别,区别在tapPromise注册的监听函数就算报错其它的监听函数都会执行,而tapAsync方式注册当其中有一个监听函数抛出错误,后续的就不执行了,原因在于tapPromise是在then方法内将_counter=0,而then是microtask,所以后面的promise会继续执行

asyncParallelHook tapPromise 与 tapAsync 当有错误抛出时,二者的表现是有区别的

asyncParallelBailHook tapPromise promise

"use strict";
var _context;
var _x = this._x;
return new Promise((function (_resolve, _reject) {
    var _sync = true;

    function _error(_err) {
        if (_sync)
            _resolve(Promise.resolve().then((function () {
                throw _err;
            })));
        else
            _reject(_err);
    };
    var _results = new Array(4);
    var _checkDone = function () {
        for (var i = 0; i < _results.length; i++) {
            var item = _results[i];
            if (item === undefined) return false;
            if (item.result !== undefined) {
                _resolve(item.result);
                return true;
            }
            if (item.error) {
                _error(item.error);
                return true;
            }
        }
        return false;
    }
    do {
        var _counter = 4;
        var _done = (function () {
            _resolve();
        });
        if (_counter <= 0) break;
        var _fn0 = _x[0];
        var _hasResult0 = false;
        var _promise0 = _fn0(asyncParallelBailHookarg1, asyncParallelBailHookarg2, asyncParallelBailHookarg3);
        if (!_promise0 || !_promise0.then)
            throw new Error('Tap function (tapPromise) did not return promise (returned ' + _promise0 + ')');
        _promise0.then((function (_result0) {
            _hasResult0 = true;
            if (_counter > 0) {
                if (0 < _results.length && (_result0 !== undefined && (_results.length = 1), (_results[0] = {
                        result: _result0
                    }), _checkDone())) {
                    _counter = 0;
                } else {
                    if (--_counter === 0) _done();
                }
            }
        }), function (_err0) {
            if (_hasResult0) throw _err0;
            if (_counter > 0) {
                if (0 < _results.length && ((_results.length = 1), (_results[0] = {
                        error: _err0
                    }), _checkDone())) {
                    _counter = 0;
                } else {
                    if (--_counter === 0) _done();
                }
            }
        });
        if (_counter <= 0) break;
        if (1 >= _results.length) {
            if (--_counter === 0) _done();
        } else {
            var _fn1 = _x[1];
            var _hasResult1 = false;
            var _promise1 = _fn1(asyncParallelBailHookarg1, asyncParallelBailHookarg2, asyncParallelBailHookarg3);
            if (!_promise1 || !_promise1.then)
                throw new Error('Tap function (tapPromise) did not return promise (returned ' + _promise1 + ')');
            _promise1.then((function (_result1) {
                _hasResult1 = true;
                if (_counter > 0) {
                    if (1 < _results.length && (_result1 !== undefined && (_results.length = 2), (_results[1] = {
                            result: _result1
                        }), _checkDone())) {
                        _counter = 0;
                    } else {
                        if (--_counter === 0) _done();
                    }
                }
            }), function (_err1) {
                if (_hasResult1) throw _err1;
                if (_counter > 0) {
                    if (1 < _results.length && ((_results.length = 2), (_results[1] = {
                            error: _err1
                        }), _checkDone())) {
                        _counter = 0;
                    } else {
                        if (--_counter === 0) _done();
                    }
                }
            });
        }
        if (_counter <= 0) break;
        if (2 >= _results.length) {
            if (--_counter === 0) _done();
        } else {
            var _fn2 = _x[2];
            var _hasResult2 = false;
            var _promise2 = _fn2(asyncParallelBailHookarg1, asyncParallelBailHookarg2, asyncParallelBailHookarg3);
            if (!_promise2 || !_promise2.then)
                throw new Error('Tap function (tapPromise) did not return promise (returned ' + _promise2 + ')');
            _promise2.then((function (_result2) {
                _hasResult2 = true;
                if (_counter > 0) {
                    if (2 < _results.length && (_result2 !== undefined && (_results.length = 3), (_results[2] = {
                            result: _result2
                        }), _checkDone())) {
                        _counter = 0;
                    } else {
                        if (--_counter === 0) _done();
                    }
                }
            }), function (_err2) {
                if (_hasResult2) throw _err2;
                if (_counter > 0) {
                    if (2 < _results.length && ((_results.length = 3), (_results[2] = {
                            error: _err2
                        }), _checkDone())) {
                        _counter = 0;
                    } else {
                        if (--_counter === 0) _done();
                    }
                }
            });
        }
        if (_counter <= 0) break;
        if (3 >= _results.length) {
            if (--_counter === 0) _done();
        } else {
            var _fn3 = _x[3];
            var _hasResult3 = false;
            var _promise3 = _fn3(asyncParallelBailHookarg1, asyncParallelBailHookarg2, asyncParallelBailHookarg3);
            if (!_promise3 || !_promise3.then)
                throw new Error('Tap function (tapPromise) did not return promise (returned ' + _promise3 + ')');
            _promise3.then((function (_result3) {
                _hasResult3 = true;
                if (_counter > 0) {
                    if (3 < _results.length && (_result3 !== undefined && (_results.length = 4), (_results[3] = {
                            result: _result3
                        }), _checkDone())) {
                        _counter = 0;
                    } else {
                        if (--_counter === 0) _done();
                    }
                }
            }), function (_err3) {
                if (_hasResult3) throw _err3;
                if (_counter > 0) {
                    if (3 < _results.length && ((_results.length = 4), (_results[3] = {
                            error: _err3
                        }), _checkDone())) {
                        _counter = 0;
                    } else {
                        if (--_counter === 0) _done();
                    }
                }
            });
        }
    } while (false);
    _sync = false;
}));

从上面最终生成的BodyCode来看, 按照taps生成对应的promise,然后陆续执行promise,当其中一个promise先返回值,还得确认该监听函数在taps中的顺序,如果顺序也满足,则会进入promise()的then回调,并将返回值返回出去,如果不满足顺序,则会等待前面的监听函数resolve,如果有一个promise抛出错误且还未有其它promise先成功,则会进入promise()的catch回调

asyncParallelBailHook tapAsync callAsync

"use strict";
var _context;
var _x = this._x;
var _results = new Array(4);
var _checkDone = function () {
    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 = 4;
    var _done = (function () {
        _callback();
    });
    if (_counter <= 0) break;
    var _fn0 = _x[0];
    _fn0(asyncParallelBailHookarg1, asyncParallelBailHookarg2, asyncParallelBailHookarg3, (function (_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 _fn1 = _x[1];
        _fn1(asyncParallelBailHookarg1, asyncParallelBailHookarg2, asyncParallelBailHookarg3, (function (_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 _fn2 = _x[2];
        _fn2(asyncParallelBailHookarg1, asyncParallelBailHookarg2, asyncParallelBailHookarg3, (function (_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 _fn3 = _x[3];
        _fn3(asyncParallelBailHookarg1, asyncParallelBailHookarg2, asyncParallelBailHookarg3, (function (_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();
                    }
                }
            }
        }));
    }
} while (false);

从上面最终生成的BodyCode来看, 按照taps调用对应的监听函数,当任何一个监听函数先抛出错误,则会导致后面的监听函数不会执行,且会触发callAsync传入的callback,并将错误参数当成第一个参数传入;当有监听函数resolve了对应的值出去,最终callback内得到的值也是,按照taps顺序来决定返回优先级的

asyncParallelBailHook tapPromise 与 tapAsync 当有错误抛出时,二者的表现是有区别的

到这里所有的钩子函数都分析完了,我们可以看出,同步及异步串行钩子最好理解,异步并行钩子不怎么好理解;不过我们都可以通过实际的例子去把new Function的BodyCode部分打印出来,然后看钩子的效果

总结

tapable的设计真的很精美,没有使用递归来控制执行顺序,而是通过new Function的方式来拼接代码执行顺序,需要悟悟其中的设计思想

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant