Appearance
tapable 事件流执行过程
首先我们知道每一个事件流在 hook.tap()、hook.tapAsync()、hook.tapPromise()是不执行 fn 回调函数的,而是在事件实例对象调用 hook.call()、hook.promise()、hook.callAsync()的时候才会触发事件流队列的执行,那么其是怎么执行的,
详情仍然是看 Hook.js
js
class Hook {
constructor(args) {
if (!Array.isArray(args)) args = [];
this._args = args;
this.taps = [];
this.interceptors = [];
// 初始化的时候 通过函数柯里化 去生成 xx.call() 处理函数
this.call = this._call = this._createCompileDelegate('call', 'sync');
this.promise = this._promise = this._createCompileDelegate('promise', 'promise');
this.callAsync = this._callAsync = this._createCompileDelegate('callAsync', 'async');
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,
});
}
_createCompileDelegate(name, type) {
// 当我们执行 h1.call(7777); 这个的时候 通过函数柯里化就可以
// 1. args 保存当前的参数 [7777]
// 2. name === 当前的 方法名 call
// 3. type === 'sync'
const lazyCompileHook = (...args) => {
debugger;
this[name] = this._createCall(type);
return this[name](...args);
};
return lazyCompileHook;
}
tap(options, fn) {}
tapAsync(options, fn) {}
tapPromise(options, fn) {}
_runRegisterInterceptors(options) {}
withOptions(options) {}
isUsed() {}
intercept(interceptor) {}
_resetCompilation() {}
_insert(item) {}
}
发现其是在初始化的时候通过函数柯里化的方式去生成 this.call、this.promise、this.callAsync 三个实例函数(lazyCompileHook(...args))
js
class Hook {
constructor(args) {
// 初始化的时候 通过函数柯里化 去生成 xx.call() 处理函数
this.call = this._call = this._createCompileDelegate('call', 'sync');
this.promise = this._promise = this._createCompileDelegate('promise', 'promise');
this.callAsync = this._callAsync = this._createCompileDelegate('callAsync', 'async');
}
_createCompileDelegate(name, type) {
// 当我们执行 h1.call(7777); 这个的时候 通过函数柯里化就可以
// 1. args 保存当前的参数 [7777]
// 2. name === 当前的 方法名 call
// 3. type === 'sync'
const lazyCompileHook = (...args) => {
debugger;
this[name] = this._createCall(type);
return this[name](...args);
};
return lazyCompileHook;
}
}
然后在我们执行 hook.call('gzh' , 20)的时候通过 this._createCall(type)去生成真正的执行函数
js
class Hook {
constructor(args) {
// 初始化的时候 通过函数柯里化 去生成 xx.call() 处理函数
this.call = this._call = this._createCompileDelegate('call', 'sync');
this.promise = this._promise = this._createCompileDelegate('promise', 'promise');
this.callAsync = this._callAsync = this._createCompileDelegate('callAsync', 'async');
}
compile(options) {
throw new Error('Abstract: should be overriden');
}
_createCall(type) {
// 调用当前的 this.compile方法
return this.compile({
taps: this.taps,
interceptors: this.interceptors,
args: this._args,
type: type,
});
}
_createCompileDelegate(name, type) {
// 当我们执行 h1.call(7777); 这个的时候 通过函数柯里化就可以
// 1. args 保存当前的参数 [7777]
// 2. name === 当前的 方法名 call
// 3. type === 'sync'
const lazyCompileHook = (...args) => {
debugger;
this[name] = this._createCall(type);
return this[name](...args);
};
return lazyCompileHook;
}
}
我们看 this._createCall(type)只是 return this.compile({}),然而我们不是使用的 Hook 去创建实例对象而是通过每一个 SyncHook、SyncBailHook、SyncLoopHook、SyncWaterfallHook、AsyncParallelBailHook、AsyncParallelHook、AsyncSeriesHook、AsyncSeriesBailHook、AsyncSeriesLoopHook、AsyncSeriesWaterfallHook 去创建的,然后我们找一个发现其都重写了其 compile(options) {}方法
js
const Hook = require('./Hook');
const HookCodeFactory = require('./HookCodeFactory');
class SyncHookCodeFactory extends HookCodeFactory {
content({ onError, onResult, 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);
}
}
在 compile 中其先调用 factory.setup(this, options);去将 taps 中的 fn 回调拿出来存放在_x 数组中
js
class HookCodeFactory {
constructor(config) {
this.config = config;
this.options = undefined;
}
/**
* 设置处理的数据 即将 options.taps 中每一个对象的 fn ,转换成一个数组
*/
setup(instance, options) {
instance._x = options.taps.map(t => t.fn);
}
}
然后调用 factory.create(options)
js
class HookCodeFactory {
create(options) {
this.init(options);
switch (this.options.type) {
case 'sync':
let code1 = '';
code1 =
'"use strict";\n debugger; \n' +
this.header() +
this.content({
onError: err => `throw ${err};\n`,
onResult: result => `return ${result};\n`,
onDone: () => '',
rethrowIfPossible: true,
});
console.log(code1);
return new Function(this.args(), code1);
case 'async':
return 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',
})
);
case 'promise':
let code = '';
code += '"use strict";\n';
code += 'return new Promise((_resolve, _reject) => {\n';
code += 'var _sync = true;\n';
code += this.header();
code += this.content({
onError: err => {
let code = '';
code += 'if(_sync)\n';
code += `_resolve(Promise.resolve().then(() => { throw ${err}; }));\n`;
code += 'else\n';
code += `_reject(${err});\n`;
return code;
},
onResult: result => `_resolve(${result});\n`,
onDone: () => '_resolve();\n',
});
code += '_sync = false;\n';
code += '});\n';
return new Function(this.args(), code);
}
}
setup(instance, options) {}
/**
* @param {{ type: "sync" | "promise" | "async", taps: Array<Tap>, interceptors: Array<Interceptor> }} options
*/
init(options) {
this.options = options;
this._args = options.args.slice();
}
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];
// 拦截器是否定义了 call
if (interceptor.call) {
// 处理拦截器的call,这个说拦截器的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;
}
callTap(tapIndex, { onError, onResult, onDone, rethrowIfPossible }) {
debugger;
let code = '';
let hasTapCached = false;
// 处理拦截器中 的 interceptor.tap
/*
var _context;
var _x = this._x;
var _taps = this.taps;
var _interceptors = this.interceptors;
_interceptors[0].call(xxx, arg2);
var _tap0 = _taps[0];
_interceptors[0].tap(_tap0);
var _fn0 = _x[0];
_fn0(xxx, arg2);
var _tap1 = _taps[1];
_interceptors[0].tap(_tap1);
var _fn1 = _x[1];
_fn1(xxx, arg2);
我们看上面生成的结果可以看出 tap 在生成可执行的字符串的时候是 在每一个事件流回调执行之前执行执行当前tap拦截,参数为每一个的事件流的 tap对象 其实就是 { name , fn ...}
var _tap0 = _taps[0];
_interceptors[0].tap(_tap0);
*/
for (let i = 0; i < this.options.interceptors.length; i++) {
const interceptor = this.options.interceptors[i];
if (interceptor.tap) {
// 原来所有的 taps是存放在 this._taps 中,下面需要多次访问那么为了提高性能就缓存一份 var _tap0 = _taps[0];
// 在上面的例子中可能不能看出问题,主要是当我们注册很多拦截器的时候
// 这时候就是
/*
var _tap0 = _taps[0];
_interceptors[0].tap(_tap0);
_interceptors[1].tap(_tap0);
_interceptors[2].tap(_tap0);
_interceptors[3].tap(_tap0);
var _fn0 = _x[0];
_fn0(xxx, arg2);
*/
if (!hasTapCached) {
code += `var _tap${tapIndex} = ${this.getTap(tapIndex)};\n`;
hasTapCached = true;
}
code += `${this.getInterceptor(i)}.tap(${interceptor.context ? '_context, ' : ''}_tap${tapIndex});\n`;
}
}
// 生成 var _fn0 = _x[0];
code += `var _fn${tapIndex} = ${this.getTapFn(tapIndex)};\n`;
const tap = this.options.taps[tapIndex];
debugger;
// 下面生成 事件流的回调函数执行字符串
// 最简单的是 _fn0(xxx, arg2);
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 += `_fn${tapIndex}(${this.args({
before: tap.context ? '_context' : undefined,
})}).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;
}
callTapsSeries({ onError, onResult, onDone, rethrowIfPossible }) {
if (this.options.taps.length === 0) return onDone();
const firstAsync = this.options.taps.findIndex(t => t.type !== 'sync');
const next = i => {
if (i >= this.options.taps.length) {
return onDone();
}
const done = () => next(i + 1);
const doneBreak = skipDone => {
if (skipDone) return '';
return onDone();
};
return this.callTap(i, {
onError: error => onError(i, error, done, doneBreak),
onResult:
onResult &&
(result => {
return onResult(i, result, done, doneBreak);
}),
onDone:
!onResult &&
(() => {
return done();
}),
rethrowIfPossible: rethrowIfPossible && (firstAsync < 0 || i < firstAsync),
});
};
return next(0);
}
callTapsLooping({ onError, onDone, rethrowIfPossible }) {
debugger;
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';
// 处理每一个拦截器的 loop 方法
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 = () => {\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}]`;
}
}
从中可以看出其主要通过将 option.taps、option.interceptors、option.type、option.args 处理成一个可以执行的代码字符串(vue 的编译流程也是将 template 通过转成 ast->静态树处理 -> 转换成可以执行的代码字符串的方式去处理的)
每一个钩子函数怎么去转换成可以执行的代码字符串的可以具体看每一个钩子函数的解析。
但是我们可以看出 其主要分为 3 中类型
sync
async
promise
sync
对于 sync 类型的事件流其编译成的可以执行的代码都是以
js
new Function(
this.args(),
`
use strict;
// this.header(); 部分
// this.content() 部分
`
);
this.header()
js
class HookCodeFactory {
/*
生成可以执行的代码字符串的头部
主要:
1. 处理 interceptors.context
2. 处理 interceptor.call
*/
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];
// 拦截器是否定义了 call
if (interceptor.call) {
// 处理拦截器的call,这个说拦截器的call是执行的时候才会执行
// 其入参也是
code += `${this.getInterceptor(i)}.call(${this.args({
before: interceptor.context ? '_context' : undefined,
})});\n`;
}
}
return code;
}
}
module.exports = HookCodeFactory;
处理拦截器的 context
处理拦截器的 call
js
// 生成下面代码
var _x = this._x;
var _taps = this.taps;
// 回调拦截器的cal 入参为传入的值
var _interceptors = this.interceptors;
_interceptors[0].call(name, age);
- this.content()
content 才是重点,在这一部分,主要是通过 onError onResult onDone rethrowIfPossible 来区别对待
串行
串行(上一个事件处理函数的返回值作为参数传递给下一个事件处理函数)
串行(如果事件处理函数执行时有一个返回值不为空(即返回值为 undefined),则跳过剩下未执行的事件处理函数(如类的名字,意义在于保险))
怎么实现串行的?
串行是通过 this.callTapsSeries() 去实现的
源码
js
class HookCodeFactory {
callTapsSeries({ onError, onResult, onDone, rethrowIfPossible }) {
if (this.options.taps.length === 0) return onDone();
const firstAsync = this.options.taps.findIndex(t => t.type !== 'sync');
const next = i => {
// 执行的下标过大处理
if (i >= this.options.taps.length) {
return onDone();
}
// 生成下一个事件流执行的函数
const done = () => next(i + 1);
const doneBreak = skipDone => {
if (skipDone) return '';
return onDone();
};
// 核心还是通过 this.callTap()去生成
return this.callTap(i, {
onError: error => onError(i, error, done, doneBreak),
onResult:
onResult &&
(result => {
return onResult(i, result, done, doneBreak);
}),
onDone:
!onResult &&
(() => {
return done();
}),
rethrowIfPossible: rethrowIfPossible && (firstAsync < 0 || i < firstAsync),
});
};
return next(0);
}
}
从上面可以发现串行的实现其实就是将一下事件流的执行函数变成一个done( )
即 const done = () => next(i + 1);
,然后在上一个函数执行完成的时候去决定是否回调 done()
如 SyncHook
其就是 onResult:undefined; onDone:(() => {return done();})
那么在 this.callTap() 的时候其只是但但的生成 _fn0(xxx, arg2);
然后就继续下一个事件流,这样就会变成
js
// this.header生成的代码
var _context;
var _x = this._x;
var _taps = this.taps;
var _interceptors = this.interceptors;
_interceptors[0].call(xxx, arg2);
// 一个事件流生成的代码
var _tap0 = _taps[0];
_interceptors[0].tap(_tap0);
var _fn0 = _x[0];
_fn0(xxx, arg2);
// 下个事件流生成的代码
var _tap1 = _taps[1];
_interceptors[0].tap(_tap1);
var _fn1 = _x[1];
_fn1(xxx, arg2);
// ...
就看见 syncHook 其实就是很简单的将每一个事件流生成一个 var _fn0 = _x[0];_fn0(xxx, arg2);
然后执行的时候从上向下顺序执行
串行(上一个事件处理函数的返回值作为参数传递给下一个事件处理函数,SyncWaterfallHook)
我们看 SyncWaterfallHook 的 content
js
return this.callTapsSeries({
onError: (i, err) => onError(err),
onResult: (i, result, next) => {
let code = '';
code += `if(${result} !== undefined) {\n`;
code += `${this._args[0]} = ${result};\n`;
code += `}\n`;
code += next();
return code;
},
onDone: () => onResult(this._args[0]),
rethrowIfPossible,
});
其定义了 onResult 就会在 if(onResult) 生成 var _result0 = _fn0(name, age);
然后执行 onResult()
js
// 对于那些依赖上一个事件流的返回值的钩子函数,其不会只是简单的 _fn0(xxx, arg2);而是 var _result0 = _fn0(xxx, arg2);
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`;
}
// 然后在最后调用各自定义的 onResult() 去处理返回值
if (onResult) {
code += onResult(`_result${tapIndex}`);
}
if (onDone) {
code += onDone();
}
那么这个生成的代码就是
js
new Function(
'name,age',
`
'use strict';
var _context;
var _x = this._x;
var _taps = this.taps;
var _interceptors = this.interceptors;
_interceptors[0].call(name, age);
var _tap0 = _taps[0];
_interceptors[0].tap(_tap0);
var _fn0 = _x[0];
var _result0 = _fn0(name, age);
if (_result0 !== undefined) {
name = _result0;
}
var _tap1 = _taps[1];
_interceptors[0].tap(_tap1);
var _fn1 = _x[1];
var _result1 = _fn1(name, age);
if (_result1 !== undefined) {
name = _result1;
}
`
);
// 可以看出相对于 syncHook 其实现串行的方式就是
// var _result1 = _fn1(name, age);
// if(_result1 !== undefined) {
// name = _result1;
// }
// 将不为空的回调返回值去覆盖第一个参数
从上面可以看出,tapable 实现串行(上一个事件处理函数的返回值作为参数传递给下一个事件处理函数)的方式是 var _result1 = _fn1(name, age);
获取上一个的返回值,然后if(_result1 !== undefined) { name = _result1; }
去覆盖传给下一个事件流的第一个参数
串行(如果事件处理函数执行时有一个返回值不为空(即返回值为 undefined),则跳过剩下未执行的事件处理函数(如类的名字,意义在于保险))
我们看 SyncBailHook 的 content
js
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,
});
其在 this.callTap() 的过程也是一样的,只是在最后
js
if (onResult) {
code += onResult(`_result${tapIndex}`);
}
处理 _result 的时候不同
同样我们先看生成的代码
js
// new Function('name,age', ``);
('use strict');
debugger;
var _context;
var _x = this._x;
var _taps = this.taps;
var _interceptors = this.interceptors;
_interceptors[0].call(name, age);
var _tap0 = _taps[0];
_interceptors[0].tap(_tap0);
var _fn0 = _x[0];
var _result0 = _fn0(name, age);
if (_result0 !== undefined) {
return _result0;
} else {
var _tap1 = _taps[1];
_interceptors[0].tap(_tap1);
var _fn1 = _x[1];
var _result1 = _fn1(name, age);
if (_result1 !== undefined) {
return _result1;
} else {
var _tap2 = _taps[2];
_interceptors[0].tap(_tap2);
var _fn2 = _x[2];
var _result2 = _fn2(name, age);
if (_result2 !== undefined) {
return _result2;
} else {
var _tap3 = _taps[3];
_interceptors[0].tap(_tap3);
var _fn3 = _x[3];
var _result3 = _fn3(name, age);
if (_result3 !== undefined) {
return _result3;
} else {
var _tap4 = _taps[4];
_interceptors[0].tap(_tap4);
var _fn4 = _x[4];
var _result4 = _fn4(name, age);
if (_result4 !== undefined) {
return _result4;
} else {
var _tap5 = _taps[5];
_interceptors[0].tap(_tap5);
var _fn5 = _x[5];
var _result5 = _fn5(name, age);
if (_result5 !== undefined) {
return _result5;
} else {
}
}
}
}
}
}
// 可以看出相对于 syncHook 其实现串行的方式就是
// var _result1 = _fn1(name, age);
// if (_result1 !== undefined) {
// return _result1;
// } else {
// }
// 将不为空的回调返回值去覆盖第一个参数
从上面可以看出,tapable 实现串行(如果事件处理函数执行时有一个返回值不为空(即返回值为 undefined),则跳过剩下未执行的事件处理函数(如类的名字,意义在于保险))的方式是 var _result1 = _fn1(name, age);
将下一个事件流的执行代码生成到 if(_result1 !== undefined) { }
中