Skip to content

Tapable原理分析(二) #73

Description

@xiaoxiaosaohuo

在tapable中,有两个基本的基类

  1. Hook
  2. HookCodeFactory

SyncHook、 SyncWaterHooks等所有的hooks都继承Hook基类。

HookCodeFactory的子类包括SyncHookCodeFactory、 SyncWaterCodeFactory 等不同的工厂构造函数,可以实例化出来不同工厂实例factory。

这个工厂实例factory的作用是,拼接生产出不同的 compile 函数,生产 compile 函数的过程,本质上就是拼接字符串,生成执行的代码。compile 函数,最终会在 call() 方法被调用。

生成代码依赖条件如下:

  • 注册的插件数量(0,一个,多个)
  • 注册的插件的种类(sync, async, promise)
  • 调用方法(sync, async, promise)
  • 参数的个数
  • 是否有interception

实例化

要使用hook要先进行实例化,而特定的hook继承自Hook类,所有的hook都是如此。

如SyncHook

class SyncHook extends Hook {
	tapAsync() {
		throw new Error("tapAsync is not supported on a SyncHook");
	}

	tapPromise() {
		throw new Error("tapPromise is not supported on a SyncHook");
	}

	compile(options) {
		factory.setup(this, options);
		return factory.create(options);
	}
}

先看基类Hook

class Hook {
	constructor(args) {
		if (!Array.isArray(args)) args = [];
		this._args = args;// 事件回调的接收参数名数组
		this.taps = [];//注册的事件对象(事件名,回调等)
		this.interceptors = [];//拦截器
		//三个触发事件的方法
		this.call = this._call;
		this.promise = this._promise;
		this.callAsync = this._callAsync;
		//存放回调函数,拼接代码时使用
		this._x = undefined;
	}
    // 根据特定的策略编译执行插件的代码
	compile(options) {
		throw new Error("Abstract: should be overriden");
	}

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

	
}



事件注册

有三种注册方式

  • tap
  • tapAsync
  • tapPromise
tap(options, fn) {
//options注册事件的名字或者其他参数,可以是字符串或者对象,最终会被处理为对象。
	if (typeof options === "string") options = { name: options };
	if (typeof options !== "object" || options === null)
		throw new Error(
			"Invalid arguments to tap(options: Object, fn: function)"
		);
	//type属性,不同的监听方式type不同
	//在之后的处理时会根据 type 的不同触发不同的逻辑。
	options = Object.assign({ type: "sync", fn: fn }, options);
	if (typeof options.name !== "string" || options.name === "")
		throw new Error("Missing name for tap");
	options = this._runRegisterInterceptors(options);
	this._insert(options);
}

tapAsync(options, fn) {
	if (typeof options === "string") options = { name: options };
	if (typeof options !== "object" || options === null)
		throw new Error(
			"Invalid arguments to tapAsync(options: Object, fn: function)"
		);
	options = Object.assign({ type: "async", fn: fn }, options);
	if (typeof options.name !== "string" || options.name === "")
		throw new Error("Missing name for tapAsync");
	options = this._runRegisterInterceptors(options);
	this._insert(options);
}

tapPromise(options, fn) {
	if (typeof options === "string") options = { name: options };
	if (typeof options !== "object" || options === null)
		throw new Error(
			"Invalid arguments to tapPromise(options: Object, fn: function)"
		);
	options = Object.assign({ type: "promise", fn: fn }, options);
	if (typeof options.name !== "string" || options.name === "")
		throw new Error("Missing name for tapPromise");
	options = this._runRegisterInterceptors(options);
	this._insert(options);
}

// 处理拦截器register属性
_runRegisterInterceptors(options) {
	for (const interceptor of this.interceptors) {
		if (interceptor.register) {
			const newOptions = interceptor.register(options);
			if (newOptions !== undefined) options = newOptions;
		}
	}
	return options;
}


isUsed() {
	return this.taps.length > 0 || this.interceptors.length > 0;
}

//重置三个调用事件的方法

_resetCompilation() {
	this.call = this._call;
	this.callAsync = this._callAsync;
	this.promise = this._promise;
}

// 根据before,stage属性排列监听事件的顺序
_insert(item) {
	this._resetCompilation();
	let before;
	// 如果 before 属性存在,把它转换成 Set 数据结构
	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--;
		//每次遍历,taps 数组中每次都会存在相邻的两个相同的值。循环结束就能找到要替换的下标。
		const x = this.taps[i];
		this.taps[i + 1] = x;
		const xStage = x.stage || 0;
		// 如果碰到传入 before 中有当前 name 的,就继续遍历,直到把 before 全部清空。
		if (before) {
			if (before.has(x.name)) {
				before.delete(x.name);
				continue;
			}
			// 如果 before 中的值没被删干净,
            // 新加入的事件回调最终会在最前面执行
			if (before.size > 0) {
				continue;
			}
		}
		// 如果当前 stage 大于传入的 stage,那么继续遍历
		if (xStage > stage) {
			continue;
		}
		i++;
		break;
	}
	// 循环结束的时候 i 就是是要赋值的那个下标
	this.taps[i] = item;
}

触发

三种触发方式

  • call
  • promise
  • callAsync

在Hook的构造器中对这三个方法进行了赋值操作。

this.call = this._call;
this.promise = this._promise;
this.callAsync = this._callAsync;

this._call,this._promise,this._callAsync,都是通过Object.defineProperties对Hook.prototype进行了重新的赋值。

Object.defineProperties(Hook.prototype, {
	_call: {
		value: createCompileDelegate("call", "sync"),
		configurable: true,
		writable: true
	},
	_promise: {
		value: createCompileDelegate("promise", "promise"),
		configurable: true,
		writable: true
	},
	_callAsync: {
		value: createCompileDelegate("callAsync", "async"),
		configurable: true,
		writable: true
	}
});

_这种方式很方便将_call方法赋值为另一个函数,而且可以直接在子类(如AsyncParallelHook)中,再利用Object.defineProperties将_call的vale赋值为null。就可以得到一个没有_call方法的子类。
promise和callAsync同理。


Object.defineProperties(Hook.prototype, {
	_call: {
		value: createCompileDelegate("call", "sync"),
		configurable: true,
		writable: true
	},
	_promise: {
		value: createCompileDelegate("promise", "promise"),
		configurable: true,
		writable: true
	},
	_callAsync: {
		value: createCompileDelegate("callAsync", "async"),
		configurable: true,
		writable: true
	}
});

createCompileDelegate

function createCompileDelegate(name, type) {
	return function lazyCompileHook(...args) {
		this[name] = this._createCall(type);
		return this[name](...args);
	};
}

平时调用call等方法就是调用lazyCompileHook,通过闭包保存了name,和type。

以call方法为例,调用call时,先执行this._createCall() 方法,把返回值赋值给 this[name] ,然后执行this[name]方法。

this.call = this._createCall(type)

第二次及之后执行 call 方法会直接执行已经编译好的静态脚本,这里用到了惰性函数来优化代码的运行性能

看到这里也可以解释_insert方法里 this._resetCompilation() 这个操作了,因为执行 call 方法时执行的是编译好的静态脚本,在添加监听函数是如果不重置,可能执行的静态脚本就不会包含当前注册的事件回调。

惰性函数优点

  • 效率高:惰性函数仅在第一次运行时执行计算逻辑,之后函数再次运行时都返回第一次执行的结果,节约了很多执行时间
  • 延迟执行

this._createCall() 里面本质是调用了this.compiler 方法,但是基类Hook上的compiler() 方法是一个空实现。这个方法是子类实现的。

compile

class SyncHookCodeFactory extends HookCodeFactory {
	content({ onError, onDone, rethrowIfPossible }) {
		return this.callTapsSeries({
			onError: (i, err) => onError(err),
			onDone,
			rethrowIfPossible
		});
	}
}

const factory = new SyncHookCodeFactory();

class SyncHook extends Hook {
	tapAsync() {
		throw new Error("tapAsync is not supported on a SyncHook");
	}

	tapPromise() {
		throw new Error("tapPromise is not supported on a SyncHook");
	}

	compile(options) {
		factory.setup(this, options);
		return factory.create(options);
	}
}

module.exports = SyncHook;

compile 方法中去,它内部使用到了 factory 变量,这个变量是实例化 SyncHookCodeFactory 类的结果,而 SyncHookCodeFactory 类继承自 HookCodeFactory 类。

在compile中调用了factory的setupcreate方法,这两个方法都来自父类HookCodeFactory

class HookCodeFactory {
  ...
    // factory.setup(this, options);
    // 注释中的 this 都是 SyncHook 的实例
    // instance = this
    // options = {
    //  taps: this.taps,
    //  interceptors: this.interceptors,
    //  args: this._args,
    //  type: type
    // }
    
    
    setup(instance, options) {
        instance._x = options.taps.map(t => t.fn);
    }
  ...
}

instance 接收的是 SyncHook 的实例,options 接收的是 Hook 类中_createCall 方法中传入的对象。

setup方法职责就是把options对象中回调函数数组保存到_x属性上。_x 属性会在静态脚本内部会发挥重要作用。


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

	create(options) {
		this.init(options);
		let fn;
		switch (this.options.type) {
			case "sync":
				fn = new Function(
					this.args(),
					'"use strict";\n' +
						this.header() +
						this.content({
							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.content({
							onError: err => `_callback(${err});\n`,
							onResult: result => `_callback(null, ${result});\n`,
							onDone: () => "_callback();\n"
						})
				);
				break;
			case "promise":
				let errorHelperUsed = false;
				const content = this.content({
					onError: err => {
						errorHelperUsed = true;
						return `_error(${err});\n`;
					},
					onResult: result => `_resolve(${result});\n`,
					onDone: () => "_resolve();\n"
				});
				let code = "";
				code += '"use strict";\n';
				code += "return new Promise((_resolve, _reject) => {\n";
				if (errorHelperUsed) {
					code += "var _sync = true;\n";
					code += "function _error(_err) {\n";
					code += "if(_sync)\n";
					code += "_resolve(Promise.resolve().then(() => { throw _err; }));\n";
					code += "else\n";
					code += "_reject(_err);\n";
					code += "};\n";
				}
				code += this.header();
				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);
	}

	/**
	 * @param {{ type: "sync" | "promise" | "async", taps: Array<Tap>, interceptors: Array<Interceptor> }} options
	 */
	init(options) {
		this.options = options;
		this._args = options.args.slice();
	}

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

	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";
		}
		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`;
			}
		}
		return code;
	}

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

	....省略代码
    //拼接参数
	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}]`;
	}
}

这里关键是通过 new Function 来生成代码。

new Function 第一个参数是函数的参数,第二个参数是函数体。

通过this.args方法拼接参数,ths.args 方法接收一个对象,对象中有 before 和 after,before 主要用于拼接 context,after 主要用于拼接回调函数(例如 callAsync 传入的回调函数)。

在生成函数体的时候调用了header方法和content方法。

header方法主要是申明一些变量,如context,_x,_taps等,在header方法中有一个this, 因为 new Function 生成的函数最终是赋值给 SyncHook 类的实例的 call 方法,所以是指向SyncHook 类的实例的。

content方法是在子类上定义的,它要是实现各个hooks的差异化代码。

根据不同的类型,content方法有不同的实现,目的是。让子类自己去写拼接的逻辑。

//syncHook
class SyncHookCodeFactory extends HookCodeFactory {
	content({ onError, onResult, onDone, rethrowIfPossible }) {
		return this.callTapsSeries({
			onError: (i, err) => onError(err),
			onDone,
			rethrowIfPossible
		});
	}
}
//syncBailHook
content({ onError, onResult, onDone, rethrowIfPossible }) {
	return this.callTapsSeries({
		onError: (i, err) => onError(err),
		onResult: (i, result, next) => `if(${result} !== undefined) {\n${onResult(result)};\n} else {\n${next()}}\n`,
		onDone,
		rethrowIfPossible
	});
}
//AsyncSeriesLoopHook
class AsyncSeriesLoopHookCodeFactory extends HookCodeFactory {
	content({ onError, onDone }) {
		return this.callTapsLooping({
			onError: (i, err, next, doneBreak) => onError(err) + doneBreak(true),
			onDone
		});
	}
}
.......

在所有的子类中,都实现了 content 方法,根据不同钩子执行流程的不同,调用了 callTapsSeries/callTapsParallel/callTapsLooping 并且会有 onError, onResult, onDone, rethrowIfPossible 这四中情况下的代码片段,callTapsSeries/callTapsParallel/callTapsLooping 都在基类的方法中,这三个方法中都会走到一个 callTap 的方法。

callTap

callTap(tapIndex, { onError, onResult, onDone, rethrowIfPossible }) {
	let code = "";
	let hasTapCached = false;
	//interceptors处理逻辑
	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];
	
	// 分为sync/async/promise类型
	
	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 += `(_err${tapIndex}, _result${tapIndex}) => {\n`;
			else cbCode += `_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(_result${tapIndex} => {\n`;
			code += `_hasResult${tapIndex} = true;\n`;
			if (onResult) {
				code += onResult(`_result${tapIndex}`);
			}
			if (onDone) {
				code += onDone();
			}
			code += `}, _err${tapIndex} => {\n`;
			code += `if(_hasResult${tapIndex}) throw _err${tapIndex};\n`;
			code += onError(`_err${tapIndex}`);
			code += "});\n";
			break;
	}
	return code;
}

callTap 内是一次函数执行的模板,也是根据调用方式的不同,分为 sync/async/promise 三种。

callTapsSeries

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 || false;
let code = "";
let current = onDone;
for (let j = this.options.taps.length - 1; j >= 0; j--) {
	const i = j;
	//异步类型使用
	const unroll = current !== onDone && this.options.taps[i].type !== "sync";
	if (unroll) {
		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;
}

this.callTap 中 onResult 和 onDone 的条件,就是说要么执行 onResult, 要么执行 onDone。

以SyncHook为例,

hook.tap({
    name: 'first',
}, (params) => {
    console.log('first')
})

hook.tap({
    name: 'second',
    before: 'first',
}, (params) => {
    console.log('second')
})

hook.tap({
 name: 'third',
 stage: -1
}, (params) => {
 console.log('third')
})

hook.call('tapable', 'siven')

其生成的代码如下:

(function anonymous(SyncHook
    ) {
    "use strict";
    var _context;
    var _x = this._x;
    var _fn0 = _x[0];
    _fn0(SyncHook);
    var _fn1 = _x[1];
    _fn1(SyncHook);
    var _fn2 = _x[2];
    _fn2(SyncHook);
    
})

callTapsLooping

循环执行的钩子会执行callTapsLooping。

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";
	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;
}

例子

hook.tap({
    name: 'first',
}, (params) => {
    console.log(params);
    console.log('first');
    
})

hook.tap({
    name: 'second',
}, (params) => {
    console.log(params);
    console.log('second');
    count ++;
    if(count>1){
        return;
    }
    return true
})

hook.tap({
 name: 'third',
}, (params) => {
console.log('third');
})

hook.call('tapable', 'siven');

生成代码如下:

(function anonymous(SyncLoopHook) {
    "use strict";
    var _context;
    var _x = this._x;
    var _loop;
    do {
        _loop = false;
        var _fn0 = _x[0];
        var _result0 = _fn0(SyncLoopHook);
        if (_result0 !== undefined) {
            _loop = true;
        } else {
            var _fn1 = _x[1];
            var _result1 = _fn1(SyncLoopHook);
            if (_result1 !== undefined) {
                _loop = true;
            } else {
                var _fn2 = _x[2];
                var _result2 = _fn2(SyncLoopHook);
                if (_result2 !== undefined) {
                    _loop = true;
                } else {
                    if (!_loop) {}
                }
            }
        }
    } while (_loop);

}
)

callTapsParallel

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

AsyncParallelHook例子

const hook = new AsyncParallelHook(["name"]);
console.time('cost');
hook.tapAsync("first", (name, callback) => {
    setTimeout(() => {
        console.log('first');
        callback();
    }, 1000);
});

hook.tapAsync("second", (name, callback) => {
    setTimeout(() => {
        console.log('second');
        callback();
    }, 2000);
});

hook.callAsync('tapable',(error, result) => {
    console.log("end", error, result);
    console.timeEnd('cost');
});



(function anonymous(name, _callback) {
    "use strict";
    var _context;
    var _x = this._x;
    do {
        var _counter = 2;
        var _done = ()=>{
            _callback();
        }
        ;
        if (_counter <= 0)
            break;
        var _fn0 = _x[0];
        _fn0(name, _err0=>{
    //这个函数是 next 函数
            //调用这个函数的时间不能确定,有可能已经执行了接下来的几个注册函数
            if (_err0) {
    //如果还没执行所有注册函数,终止
                if (_counter > 0) {
                    _callback(_err0);
                    _counter = 0;
                }
            } else {
    //检查 _counter 的值,如果是 0 的话,则结束
                //同样,由于函数实际调用时间无法确定,需要检查是否已经运行完毕
                if (--_counter === 0)
                    _done();
            }
        }
        );
        if (_counter <= 0)
            break;
        var _fn1 = _x[1];

        _fn1(name, _err1=>{
            if (_err1) {
                if (_counter > 0) {
                    _callback(_err1);
                    _counter = 0;
                }
            } else {
                if (--_counter === 0)
                    _done();
            }
        }
        );
    } while (false);

}
)

总结一下 callTap 中实现了 sync/promise/async 三种基本的一次函数执行的模板,同时将涉及函数执行流程的代码 onError/onDone/onResult 部分抽离出来, 在 callTapsSeries/callTapsLooping/callTapsParallel 中,通过传入不同的 onError/onDone/onResult 实现出不同流程的模板 。

生成代码之后便是把 new Function 生成的函数返回给 call 方法,然后让 call 方法去执行静态脚本了。

其他hooks生成代码示例。

hook.tap({
    name: 'first',
}, (params) => {
    console.log(params);
    return 'first';
})

hook.tap({
    name: 'second',
}, (params) => {
    console.log(params);
    return 'second';
})

hook.tap({
 name: 'third',
}, (params) => {
console.log(params);
 return 'third';
})

hook.call('tapable', 'siven')

<!--生成代码:-->

(function anonymous(SyncWaterfallHook) {
    "use strict";
    var _context;
    var _x = this._x;
    var _fn0 = _x[0];
    var _result0 = _fn0(SyncWaterfallHook);
    if (_result0 !== undefined) {
        SyncWaterfallHook = _result0;
    }
    var _fn1 = _x[1];
    var _result1 = _fn1(SyncWaterfallHook);
    if (_result1 !== undefined) {
        SyncWaterfallHook = _result1;
    }
    var _fn2 = _x[2];
    var _result2 = _fn2(SyncWaterfallHook);
    if (_result2 !== undefined) {
        SyncWaterfallHook = _result2;
    }
    return SyncWaterfallHook;

}
)

SyncBailHook

hook.tap({
    name: 'first',
}, (params) => {
    console.log('first')
})

hook.tap({
    name: 'second',
}, (params) => {
    console.log('second');
    return 'second return';
})

hook.tap({
 name: 'third',
}, (params) => {
 console.log('third')
})

hook.call('tapable', 'siven')


(function anonymous(SyncBailHook) {
    "use strict";
    var _context;
    var _x = this._x;
    var _fn0 = _x[0];
    var _result0 = _fn0(SyncBailHook);
    if(_result0 !== undefined) {
        return _result0;
    } else {
        var _fn1 = _x[1];
        var _result1 = _fn1(SyncBailHook);
        if(_result1 !== undefined) {
        return _result1;
    } else {
        var _fn2 = _x[2];
        var _result2 = _fn2(SyncBailHook);
        if(_result2 !== undefined) {
            return _result2;
        } else {
            }
        }
    }
    
})

AsyncSeriesHook

hook.tapAsync("first", (name, callback) => {
  console.log("first", name);
  callback();
});

hook.tapAsync("second", (name, callback) => {
  console.log("second", name);

  callback("second error", "second result");
});

hook.tapAsync("third", (name, callback) => {
  console.log("third", name);
  callback(null,"third");
});


hook.callAsync("callAsync", (error, result) => {
  console.log("callAsync", error, result);
});



<!--生成代码-->

 (function anonymous(name, _callback
    ) {
    "use strict";
    var _context;
    var _x = this._x;
    function _next1() {
        var _fn2 = _x[2];
        _fn2(name, _err2 => {
            if(_err2) {
                _callback(_err2);
            } else {
                _callback();
            }
        });
    }
    function _next0() {
        var _fn1 = _x[1];
            _fn1(name, _err1 => {
            if(_err1) {
                _callback(_err1);
            } else {
                _next1();
            }
        });
    }

    var _fn0 = _x[0];

    _fn0(name, _err0 => {
        if(_err0) {
            _callback(_err0);
        } else {
            _next0();
        }
    });

})

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions