Webpack-Tapable
TIP
Webpack 本质上是一种事件流的机制,它的工作流程就是将各个插件串联起来,而实现这一切的核心就是 Tapable,webpack 中最核心的负责编译的 Compiler 和负责创建 bundle 的 Compilation 都是 Tapable 的实例
一 简介
- tapable 是一个类似于 Node.js 中的 EventEmitter的库,但更专注于自定义事件的触发和处理
- webpack 通过 tapable 将实现与流程解耦,所有具体实现通过插件的形式存在
1 导出的钩子类
# 一共九个钩子
const {
SyncHook,
SyncBailHook,
SyncWaterfallHook,
SyncLoopHook,
AsyncParallelHook,
AsyncParallelBailHook,
AsyncSeriesHook,
AsyncSeriesBailHook,
AsyncSeriesWaterfallHook
} = require("tapable");
2 触发方式
类型 | 说明 |
---|---|
Sync | 同步钩子,Sync开头的Hook类只能用tap方法注册事件回调 |
AsyncParallel | 异步并行,同时开始执行注册的所有事件回调函数 |
AsyncSeries | 异步串行,按照顺序依次执行注册的所有事件回调函数 |
3 运行逻辑
类型 | 说明 |
---|---|
Basic | 基础类型,单纯的调用注册的事件回调,不关心内部的运行逻辑 |
Bail | 保险类型,当一个事件回调在运行时返回值不为undefined时,停止后面事件回调的执行 |
Loop | 循环类型,如果当前执行的事件回调的发回执不是undefined,那么重新从第一个注册的事件回调处执行,直到全部执行完成 |
Waterfall | 瀑布类型,如果当前执行的事件回调的返回值不为undefined,那么就把下一个事件回调的第一个参数替换成这个值 |
4 具体分类
按同步异步分类
- Hook 类型可以分为同步Sync和异步Async,异步又分为并行和串行
按返回值分类
二 使用
- 所有的构造函数都接收一个可选参数,参数是一个参数名的字符串数组
- 参数的名字可以任意填写,但是参数数组的长数必须要根实际接受的参数个数一致
- 如果回调函数不接受参数,可以传入空数组
- 在实例化的时候传入的数组长度,长度有用,值没有用途
- 执行 call 时,参数个数和实例化时的数组长度有关
- 回调的时候是按先入先出的顺序执行的,先放的先执行
1 SyncHook
- 回调函数是按顺序执行的,全部执行完毕
const {SyncHook} = require('tapable');
const hook = new SyncHook(['name', 'age']);
hook.tap('1', (name, age) => {
console.log(1, name, age);
})
hook.tap('2', (name, age) => {
console.log(2, name, age);
})
hook.tap('3', (name, age) => {
console.log(3, name, age);
})
hook.tap('4', (name, age) => {
console.log(4, name, age);
})
hook.call('test', 10);
1 'test' 10
2 'test' 10
3 'test' 10
4 'test' 10
2 SyncBailHook
- 回调函数是按顺序执行的,当返回值 不等于 undefined的时候,就停止调用后续的回调,然后结束
const {SyncBailHook} = require('tapable');
const hook = new SyncBailHook(['name', 'age']);
hook.tap('1', (name, age) => {
console.log(1, name, age);
})
hook.tap('2', (name, age) => {
console.log(2, name, age);
return null; // 返回值不为undefined,跳到结束
})
hook.tap('3', (name, age) => {
console.log(3, name, age);
})
hook.tap('4', (name, age) => {
console.log(4, name, age);
})
hook.call('test', 10);
1 'test' 10
2 'test' 10
3 SyncWaterfallHook
- 回调函数是按顺序执行的,表示如果上一个回调函数返回结果 不等于 undefined,则会作为下一个回调函数的第一个参数,直到全部执行完毕,然后结束
const {SyncWaterfallHook} = require('tapable');
const hook = new SyncWaterfallHook(['name', 'age']);
hook.tap('1', (name, age) => {
console.log(1, name, age);
return '1';
})
hook.tap('2', (name, age) => {
console.log(2, name, age);
})
hook.tap('3', (name, age) => {
console.log(3, name, age);
return '3';
})
hook.tap('4', (name, age) => {
console.log(4, name, age);
})
hook.call('test', 10);
1 'test' 10
2 '1' 10
3 '1' 10
4 '3' 10
4 SyncLoopHook
- 特点是不停的循环执行回调函数,直到每个回调函数结果等于 undefined,就进行下一个,如果不是则从头开始循环
- 要注意的是每次循环都是从头开始循环的
const {SyncLoopHook} = require('tapable');
const hook = new SyncLoopHook(['name', 'age']);
let counter1 = 0;
let counter2 = 0;
let counter3 = 0;
hook.tap('1', (name, age) => {
console.log(1, 'counter1', counter1);
if (++counter1 == 1) {
counter1 = 0;
return;
}
return true;
})
hook.tap('2', (name, age) => {
console.log(2, 'counter2', counter2);
if (++counter2 == 2) {
counter2 = 0;
return;
}
return true;
})
hook.tap('3', (name, age) => {
console.log(3, 'counter3', counter3);
if (++counter3 == 3) {
counter3 = 0;
return;
}
return true;
})
hook.call('test', 10);
1 'counter1' 0
2 'counter2' 0
1 'counter1' 0
2 'counter2' 1
3 'counter3' 0
1 'counter1' 0
2 'counter2' 0
1 'counter1' 0
2 'counter2' 1
3 'counter3' 1
1 'counter1' 0
2 'counter2' 0
1 'counter1' 0
2 'counter2' 1
3 'counter3' 2
5 AsyncParallelHook
- 异步并行钩子,所有注册的回调同时开始执行
- 分三种注册方式:同步注册、异步回调注册、异步promise注册
- 不管有无返回值,都会全部执行完毕
(1) tap:同步注册
const {AsyncParallelHook} = require('tapable');
const hook = new AsyncParallelHook(['name', 'age']);
console.time('cost');
// 同步注册
hook.tap('1', (name, age) => {
console.log(1, name, age);
})
hook.tap('2', (name, age) => {
console.log(2, name, age);
})
hook.tap('3', (name, age) => {
console.log(3, name, age);
})
hook.callAsync('test', 10, (err) => {
console.log(err);
console.timeEnd('cost');
});
1 'test' 10
2 'test' 10
3 'test' 10
undefined
cost: 3.786ms
(2) tapAsync:异步注册
const {AsyncParallelHook} = require('tapable');
const hook = new AsyncParallelHook(['name', 'age']);
console.time('cost');
// 异步回调注册
hook.tapAsync('1', (name, age, callback) => {
setTimeout(() => {
console.log(1, name, age);
callback();
}, 1000);
})
hook.tapAsync('2', (name, age, callback) => {
setTimeout(() => {
console.log(2, name, age);
callback();
}, 2000);
})
hook.tapAsync('3', (name, age, callback) => {
setTimeout(() => {
console.log(3, name, age);
callback();
}, 3000);
})
hook.callAsync('test', 10, err => {
console.log(err);
console.timeEnd('cost');
})
1 'test' 10
2 'test' 10
3 'test' 10
undefined
cost: 3002.394ms
(3) tapPromise:异步注册
const {AsyncParallelHook} = require('tapable');
const hook = new AsyncParallelHook(['name', 'age']);
console.time('cost');
// 异步promise回调
hook.tapPromise('1', (name, age) => {
return new Promise(resolve => {
setTimeout(() => {
console.log(1, name, age);
resolve();
}, 1000);
})
})
hook.tapPromise('2', (name, age) => {
return new Promise(resolve => {
setTimeout(() => {
console.log(2, name, age);
resolve();
}, 2000);
})
})
hook.tapPromise('3', (name, age) => {
return new Promise(resolve => {
setTimeout(() => {
console.log(3, name, age);
resolve();
}, 3000);
})
})
hook.promise('test', 10).then(result => {
console.log(result);
console.timeEnd('cost');
})
1 'test' 10
2 'test' 10
3 'test' 10
undefined
cost: 3002.135ms
6 AsyncParallelBailHook
- 带保险的异步并行钩子,所有注册的回调同时开始执行,如果有一个任务返回值不为空,就直接结束
(1) tap:同步注册
const {AsyncParallelBailHook} = require('tapable');
const hook = new AsyncParallelBailHook(['name', 'age']);
console.time('cost');
// 同步注册 - 如果有一个任务有返回值则调用最终的回调
hook.tap('1', (name, age) => {
console.log(1, name, age);
})
hook.tap('2', (name, age) => {
console.log(2, name, age);
return 'result';
})
hook.tap('3', (name, age) => {
console.log(3, name, age);
})
hook.callAsync('test', 10, (err) => {
console.log(err);
console.timeEnd('cost');
});
1 'test' 10
2 'test' 10
null
cost: 3.818ms
(2) tapAsync:异步注册
const {AsyncParallelBailHook} = require('tapable');
const hook = new AsyncParallelBailHook(['name', 'age']);
console.time('cost');
// 异步回调注册 - 有一个任务回调函数中有返回值,就直接调最终的回调
hook.tapAsync('1', (name, age, callback) => {
setTimeout(() => {
console.log(1, name, age);
callback();
}, 1000);
})
hook.tapAsync('2', (name, age, callback) => {
setTimeout(() => {
console.log(2, name, age);
callback('result');
}, 2000);
})
hook.tapAsync('3', (name, age, callback) => {
setTimeout(() => {
console.log(3, name, age);
callback();
}, 3000);
})
hook.callAsync('test', 10, err => {
console.log(err);
console.timeEnd('cost');
})
1 'test' 10
2 'test' 10
result
cost: 2004.420ms
3 'test' 10
(3) tapPromise:异步注册
const {AsyncParallelBailHook} = require('tapable');
const hook = new AsyncParallelBailHook(['name', 'age']);
console.time('cost');
// 异步promise回调 - 只要有一个任务有 resolve 或者 reject 值,不管成功失败都结束
hook.tapPromise('1', (name, age) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(1, name, age);
resolve();
}, 1000);
})
})
hook.tapPromise('2', (name, age) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(2, name, age);
resolve('成功');
// reject('失败');
}, 2000);
})
})
hook.tapPromise('3', (name, age) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(3, name, age);
resolve();
}, 3000);
})
})
hook.promise('test', 10).then(result => {
console.log(result);
console.timeEnd('cost');
})
1 'test' 10
2 'test' 10
成功
cost: 2002.886ms
3 'test' 10
7 AsyncSeriesHook
- 异步串行钩子,任务一个一个执行,执行完上一个执行下一个
(1) tap:同步注册
const {AsyncSeriesHook} = require('tapable');
const hook = new AsyncSeriesHook(['name', 'age']);
console.time('cost');
// 同步注册
hook.tap('1', (name, age) => {
console.log(1, name, age);
})
hook.tap('2', (name, age) => {
console.log(2, name, age);
})
hook.tap('3', (name, age) => {
console.log(3, name, age);
})
hook.callAsync('test', 10, (err) => {
console.log(err);
console.timeEnd('cost');
});
1 'test' 10
2 'test' 10
3 'test' 10
undefined
cost: 3.779ms
(2) tapAsync:异步注册
const {AsyncSeriesHook} = require('tapable');
const hook = new AsyncSeriesHook(['name', 'age']);
console.time('cost');
// 异步回调注册
hook.tapAsync('1', (name, age, callback) => {
setTimeout(() => {
console.log(1, name, age);
callback();
}, 1000);
})
hook.tapAsync('2', (name, age, callback) => {
setTimeout(() => {
console.log(2, name, age);
callback();
}, 2000);
})
hook.tapAsync('3', (name, age, callback) => {
setTimeout(() => {
console.log(3, name, age);
callback();
}, 3000);
})
hook.callAsync('test', 10, err => {
console.log(err);
console.timeEnd('cost');
})
1 'test' 10
2 'test' 10
3 'test' 10
undefined
cost: 6006.636ms
(3) tapPromise:异步注册
const {AsyncSeriesHook} = require('tapable');
const hook = new AsyncSeriesHook(['name', 'age']);
console.time('cost');
// 异步promise回调
hook.tapPromise('1', (name, age) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(1, name, age);
resolve();
}, 1000);
})
})
hook.tapPromise('2', (name, age) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(2, name, age);
resolve();
}, 2000);
})
})
hook.tapPromise('3', (name, age) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(3, name, age);
resolve();
}, 3000);
})
})
hook.promise('test', 10).then(result => {
console.log(result);
console.timeEnd('cost');
})
1 'test' 10
2 'test' 10
3 'test' 10
undefined
cost: 6006.171ms
8 AsyncSeriesBailHook
- 带保险的异步串行钩子,只要有一个返回了不为 undefined 的值就直接结束
(1) tap:同步注册
const {AsyncSeriesBailHook} = require('tapable');
const hook = new AsyncSeriesBailHook(['name', 'age']);
console.time('cost');
// 同步注册
hook.tap('1', (name, age) => {
console.log(1, name, age);
})
hook.tap('2', (name, age) => {
console.log(2, name, age);
return 'result';
})
hook.tap('3', (name, age) => {
console.log(3, name, age);
})
hook.callAsync('test', 10, (err) => {
console.log(err);
console.timeEnd('cost');
});
1 'test' 10
2 'test' 10
null
cost: 3.820ms
(2) tapAsync:异步注册
const {AsyncSeriesBailHook} = require('tapable');
const hook = new AsyncSeriesBailHook(['name', 'age']);
console.time('cost');
// 异步回调注册
hook.tapAsync('1', (name, age, callback) => {
setTimeout(() => {
console.log(1, name, age);
callback();
}, 1000);
})
hook.tapAsync('2', (name, age, callback) => {
setTimeout(() => {
console.log(2, name, age);
callback('result');
}, 2000);
})
hook.tapAsync('3', (name, age, callback) => {
setTimeout(() => {
console.log(3, name, age);
callback();
}, 3000);
})
hook.callAsync('test', 10, err => {
console.log(err);
console.timeEnd('cost');
})
1 'test' 10
2 'test' 10
result
cost: 3007.767ms
(3) tapPromise:异步注册
const {AsyncSeriesBailHook} = require('tapable');
const hook = new AsyncSeriesBailHook(['name', 'age']);
console.time('cost');
// 异步promise回调
hook.tapPromise('1', (name, age) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(1, name, age);
resolve();
}, 1000);
})
})
hook.tapPromise('2', (name, age) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(2, name, age);
// resolve('成功');
reject('失败');
}, 2000);
})
})
hook.tapPromise('3', (name, age) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(3, name, age);
resolve();
}, 3000);
})
})
//Promise.all();
hook.promise('test', 10).then(result => {
console.log(result);
console.timeEnd('cost');
}, err => {
console.log(err);
console.timeEnd('cost');
})
1 'test' 10
2 'test' 10
失败
cost: 3005.142ms
9 AsyncSeriesWaterfallHook
- 瀑布模型的异步串行钩子,按照顺序依次执行任务,上一个函数的返回值为下一个函数的第一个参数
(1) tap:同步注册
const {AsyncSeriesWaterfallHook} = require('tapable');
const hook = new AsyncSeriesWaterfallHook(['name', 'age']);
console.time('cost');
// 同步注册
hook.tap('1', (name, age) => {
console.log(1, name, age);
})
hook.tap('2', (name, age) => {
console.log(2, name, age);
return '结果2';
})
hook.tap('3', (name, age) => {
console.log(3, name, age);
})
hook.callAsync('test', 10, (err) => {
console.log(err);
console.timeEnd('cost');
});
1 'test' 10
2 'test' 10
3 '结果2' 10
null
cost: 3.723ms
(2) tapAsync:异步注册
const {AsyncSeriesWaterfallHook} = require('tapable');
const hook = new AsyncSeriesWaterfallHook(['name', 'age']);
console.time('cost');
// 异步回调注册
hook.tapAsync('1', (name, age, callback) => {
setTimeout(() => {
console.log(1, name, age);
callback(null, 1);
}, 1000);
})
hook.tapAsync('2', (number, age, callback) => {
setTimeout(() => {
console.log(2, number, age);
callback(null, ++number);
}, 2000);
})
hook.tapAsync('3', (number, age, callback) => {
setTimeout(() => {
console.log(3, number, age);
callback(null, ++number);
}, 3000);
})
hook.callAsync('test', 10, (err, data) => {
console.log(err, data);
console.timeEnd('cost');
})
1 'test' 10
2 1 10
3 2 10
null 3
cost: 6007.065ms
(3) tapPromise:异步注册
const {AsyncSeriesWaterfallHook} = require('tapable');
const hook = new AsyncSeriesWaterfallHook(['name', 'age']);
console.time('cost');
// 异步promise回调
hook.tapPromise('1', (name, age) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(1, name, age);
resolve(1);
}, 1000);
})
})
hook.tapPromise('2', (number, age) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(2, number, age);
resolve(++number);
}, 2000);
})
})
hook.tapPromise('3', (number, age) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(3, number, age);
resolve(++number);
}, 3000);
})
})
//Promise.all();
hook.promise('test', 10).then(result => {
console.log(result);
console.timeEnd('cost');
}, err => {
console.log(err);
console.timeEnd('cost');
})
1 'test' 10
2 1 10
3 2 10
3
cost: 6004.874ms
三 源码
1 SyncHook
- 原理:先放入一个数组中,然后执行得时候利用new Function编译出一个临时的函数,然后执行,得到最终的效果
- 关键点:
- 所有的构造函数都接收一个可选参数,参数是一个参数名的字符串数组
- 参数的名字可以任意填写,但是参数数组的长数必须要根实际接受的参数个数一致
- 如果回调函数不接受参数,可以传入空数组
- 在实例化的时候传入的数组长度长度有用,值没有用途
- 执行 call 时,参数个数和实例化时的数组长度有关
- 回调的时候是按先入先出的顺序执行的,先放的先执行
- 原理图:
使用
const {SyncHook} = require('./tapable');
const hook = new SyncHook(['name', 'age']);
hook.tap('1', (name, age) => {
console.log(1, name, age);
})
hook.tap('2', (name, age) => {
console.log(2, name, age);
})
hook.tap('3', (name, age) => {
console.log(3, name, age);
})
hook.call('test', 10);
临时函数
(function anonymous(name, age) {
var _x = this._x;
var _fn0 = _x[0];
_fn0(name, age);
var _fn1 = _x[1];
_fn1(name, age);
var _fn2 = _x[2];
_fn2(name, age);
})
代码
- tapable\index.js
// 引入模块
let SyncHook = require('./SyncHook');
exports.SyncHook = SyncHook;
- tapable\SyncHook.js
const Hook = require("./Hook");
const HookCodeFactory = require("./HookCodeFactory");
class SyncHookCodeFactory extends HookCodeFactory {
content({onDone}){
return this.callTapsSeries({onDone})
}
}
let factory = new SyncHookCodeFactory();
class SyncHook extends Hook {
compile(options) {
factory.setup(this, options);
return factory.create(options);
}
}
// 导出构造函数
module.exports = SyncHook;
- tapable\Hook.js
class Hook {
constructor(args) {
if (!Array.isArray(args)) args = [];
this._args = args; // 存放着形参数组 ['name','age']
this.taps = []; // 存放所有回调函数的数组
this.call = CALL_DELEGATE; // 惰性函数
}
tap(options, fn) {
this._tap('sync', options, fn);
}
_tap(type, options, fn) {
// 如果是字符串,需要转化为对象
if (typeof options === 'string') {
options = {name: options};
}
let tapInfo = {...options, type, fn};
this._insert(tapInfo);
}
_resetCompilation(){
this.call = CALL_DELEGATE;
}
_insert(tapInfo) {
// 每次需要重置call方法
this._resetCompilation();
// 放入数组中
this.taps.push(tapInfo);
}
compile(options) {
throw new Error("Abstract: should be overridden");
}
_createCall(type) {
// 编译
return this.compile({
taps: this.taps,
args: this._args,
type
});
}
}
//创建一个call的代理方法
const CALL_DELEGATE = function(...args) {
//动态的创建call方法,并且赋给this.call
this.call = this._createCall("sync");
//执行动态创建出来的call方法
return this.call(...args);
};
module.exports = Hook;
- tapable\HookCodeFactory.js
class HookCodeFactory {
// 初始化
setup(hookInstance, options) {
// 映射出来一个回调函数的数组赋给 hooks实例的_x属性
// _x才是真正回调函数的数组
hookInstance._x = options.taps.map(item => item.fn);
}
// 初始化
init(options) {
this.options = options;
}
// 删除
deinit() {
this.options = null;
}
// 处理参数
args() {
if (Array.isArray(this.options.args))
return this.options.args.join(',');
return '';
}
// 拼接头部
header() {
let code = "";
code += "var _x = this._x;\n";//_x是回调函数的数组
return code;
}
create(options) {
this.init(options);
let fn;
switch(this.options.type) {
case 'sync':
fn = new Function(
this.args(),
this.header() + this.content({
onDone: () => ''
})
);
break;
}
this.deinit();
return fn;
}
// 简单写法
// callTapsSeries() {
// let code = this.options.taps.map((item, index) => `
// var _fn${index} = _x[${index}];
// _fn${index}(${this.args()});
// `).join('\n');
// return code;
// }
// 源码写法
callTapsSeries({onDone}) {
if (this.options.taps.length === 0) {
return onDone();
}
let code = '';
let current = onDone;
for(let j = this.options.taps.length - 1; j >= 0; j--) {
const content = this.callTap(j, {onDone: current});
current = () => content;
}
code += current();
return code;
}
callTap(tapIndex, {onDone}) {
let code = '';
code += `var _fn${tapIndex} = _x[${tapIndex}];`;
let tap = this.options.taps[tapIndex];
switch(tap.type) {
case 'sync' :
code += `_fn${tapIndex}(${this.args()});`
if (onDone) {
code += onDone();
}
break;
default:
break;
}
return code;
}
}
module.exports = HookCodeFactory;
2 AsyncParallelHook-callAsync
- 原理:利用计数器,在每个函数执行完毕完毕后,都调用一次计数器,判断计数器为0的时候,就执行最终的回调函数
使用
const {AsyncParallelHook} = require('./tapable');
const hook = new AsyncParallelHook(['name', 'age']);
console.time('cost');
// 异步回调注册
hook.tapAsync('1', (name, age, callback) => {
setTimeout(() => {
console.log(1, name, age);
callback();
}, 1000);
})
hook.tapAsync('2', (name, age, callback) => {
setTimeout(() => {
console.log(2, name, age);
callback();
}, 2000);
})
hook.tapAsync('3', (name, age, callback) => {
setTimeout(() => {
console.log(3, name, age);
callback();
}, 3000);
})
// 异步调用
hook.callAsync('test', 10, err => {
console.log(err);
console.timeEnd('cost');
})
临时函数
(function anonymous(name, age, _callback) {
var _x = this._x;
var _counter = 3; // 计数器
var _done = function () {
_callback();
};
var _fn0 = _x[0];
_fn0(name, age, function (_err0) {
if (_err0) {
_callback(_err0);
} else {
if (--_counter === 0) _done();
}
});
var _fn1 = _x[1];
_fn1(name, age, function (_err1) {
if (_err1) {
_callback(_err1);
} else {
if (--_counter === 0) _done();
}
});
var _fn2 = _x[2];
_fn2(name, age, function (_err2) {
if (_err2) {
_callback(_err2);
} else {
if (--_counter === 0) _done();
}
});
})
代码
- tapable\index.js
// 引入模块
let SyncHook = require('./SyncHook');
exports.SyncHook = SyncHook;
// 异步钩子实现
let AsyncParallelHook = require('./AsyncParallelHook');
exports.AsyncParallelHook = AsyncParallelHook;
- tapable\AsyncParallelHook.js
const Hook = require("./Hook");
const HookCodeFactory = require("./HookCodeFactory");
class SyncHookCodeFactory extends HookCodeFactory {
content({onDone}){
// 并行
return this.callTapsParallel({onDone})
}
}
let factory = new SyncHookCodeFactory();
class AsyncParallelHook extends Hook {
compile(options) {
factory.setup(this, options);
return factory.create(options);
}
}
// 导出构造函数
module.exports = AsyncParallelHook;
- tapable\Hook.js
class Hook {
constructor(args) {
if (!Array.isArray(args)) args = [];
this._args = args; // 存放着形参数组 ['name','age']
this.taps = []; // 存放所有回调函数的数组
this.call = CALL_DELEGATE;
// 添加异步方法
this.callAsync = CALL_ASYNC_DELEGATE;
}
tap(options, fn) {
this._tap('sync', options, fn);
}
tapAsync(options, fn) {
this._tap('async', options, fn);
}
_tap(type, options, fn) {
// 如果是字符串,需要转化为对象
if (typeof options === 'string') {
options = {name: options};
}
let tapInfo = {...options, type, fn};
this._insert(tapInfo);
}
_resetCompilation(){
this.call = CALL_DELEGATE;
}
_insert(tapInfo) {
// 每次需要重置call方法
this._resetCompilation();
this.taps.push(tapInfo);
}
compile(options) {
throw new Error("Abstract: should be overridden");
}
_createCall(type) {
return this.compile({
taps: this.taps,
args: this._args,
type
});
}
}
//创建一个call的代理方法
const CALL_DELEGATE = function(...args) {
//动态的创建call方法,并且赋给this.call
this.call = this._createCall("sync");
//执行动态创建出来的call方法
return this.call(...args);
};
const CALL_ASYNC_DELEGATE = function(...args) {
this.callAsync = this._createCall("async");
return this.callAsync(...args);
};
module.exports = Hook;
- tapable\HookCodeFactory.js
class HookCodeFactory {
// 初始化
setup(hookInstance, options) {
// 映射出来一个回调函数的数组赋给 hooks实例的_x属性
// _x才是真正回调函数的数组
hookInstance._x = options.taps.map(item => item.fn);
}
init(options) {
this.options = options;
}
deinit() {
this.options = null;
}
// 参数处理
args(options = {}) {
let {before, after} = options;
let allArgs = this.options.args || []; //原始参数
if (before) allArgs = [before, ...allArgs];
if (after) allArgs = [...allArgs, after];
if (allArgs.length > 0) {
return allArgs.join(', ');
}
return '';
}
header() {
let code = "";
code += "var _x = this._x;\n";//_x是回调函数的数组
return code;
}
create(options) {
this.init(options);
let fn;
switch(this.options.type) {
case 'sync':
fn = new Function(
this.args(),
this.header() + this.content({
onDone: () => ''
})
);
break;
case 'async':
fn = new Function(
this.args({after:'_callback'}),
this.header()+this.content({
onDone:()=>"_callback();\n"
})
)
break;
}
this.deinit();
return fn;
}
callTapsSeries({onDone}) {
if (this.options.taps.length === 0) {
return onDone();
}
let code = '';
let current = onDone;
for(let j = this.options.taps.length - 1; j >= 0; j--) {
const content = this.callTap(j, {onDone: current});
current = () => content;
}
code += current();
// 顺序需要调试一下,
// console.log(code);
return code;
}
callTapsParallel({onDone}) {
let code = `var _counter = ${this.options.taps.length};\n`;
if (onDone) {
code += `
var _done = function() {
${onDone()}
}
`;
}
for (let i = 0; i < this.options.taps.length; i++) {
const done = () => `if (--_counter === 0) _done()`;
code += this.callTap(i, {onDone: done});
}
return code;
}
callTap(tapIndex, {onDone}) {
let code = '';
code += `var _fn${tapIndex} = _x[${tapIndex}];`;
let tap = this.options.taps[tapIndex];
switch(tap.type) {
case 'sync' :
code += `_fn${tapIndex}(${this.args()});`
if (onDone) {
code += onDone();
}
break;
case 'async':
let cbCode = `
function (_err${tapIndex}) {
if (_err${tapIndex}) {
_callback(_err${tapIndex});
} else {
${onDone()}
}
}
`;
code += `_fn${tapIndex}(${this.args({after:cbCode})});`;
break;
default:
break;
}
console.log(code);
return code;
}
}
module.exports = HookCodeFactory;
3 AsyncParallelHook-callPromise
- 原理:利用计数器,在每个函数执行完毕完毕后,都调用一次计数器,判断计数器为0的时候,就执行最终的回调函数
- 不同点在于 promise是全部包裹在一个promise函数中,类似Promise.all方法,全部成功了才调用resolve
使用
const {AsyncParallelHook} = require('./tapable');
const hook = new AsyncParallelHook(['name', 'age']);
console.time('cost');
// 异步promise回调
hook.tapPromise('1', (name, age) => {
return new Promise(resolve => {
setTimeout(() => {
console.log(1, name, age);
resolve();
}, 1000);
})
})
hook.tapPromise('2', (name, age) => {
return new Promise(resolve => {
setTimeout(() => {
console.log(2, name, age);
resolve();
}, 2000);
})
})
hook.tapPromise('3', (name, age) => {
return new Promise(resolve => {
setTimeout(() => {
console.log(3, name, age);
resolve();
}, 3000);
})
})
hook.promise('test', 10).then(result => {
console.log(result);
console.timeEnd('cost');
})
临时函数
(function anonymous(name, age) {
var _x = this._x;
return new Promise(function (_resolve, _reject) {
var _counter = 3;
var _done = function () {
_resolve();
};
var _fn0 = _x[0];
var _promise0 = _fn0(name, age);
_promise0.then(
function () {
if (--_counter === 0) _done();
}
);
var _fn1 = _x[1];
var _promise1 = _fn1(name, age);
_promise1.then(
function () {
if (--_counter === 0) _done();
}
);
var _fn2 = _x[2];
var _promise2 = _fn0(name, age);
_promise2.then(
function () {
if (--_counter === 0) _done();
}
);
});
});
代码
- tapable\index.js
// 引入模块
let SyncHook = require('./SyncHook');
exports.SyncHook = SyncHook;
// 异步钩子实现
let AsyncParallelHook = require('./AsyncParallelHook');
exports.AsyncParallelHook = AsyncParallelHook;
- tapable\AsyncParallelHook.js
const Hook = require("./Hook");
const HookCodeFactory = require("./HookCodeFactory");
class SyncHookCodeFactory extends HookCodeFactory {
content({onDone}){
// 并行
return this.callTapsParallel({onDone})
}
}
let factory = new SyncHookCodeFactory();
class AsyncParallelHook extends Hook {
compile(options) {
factory.setup(this, options);
return factory.create(options);
}
}
// 导出构造函数
module.exports = AsyncParallelHook;
- tapable\Hook.js
class Hook {
constructor(args) {
if (!Array.isArray(args)) args = [];
this._args = args; // 存放着形参数组 ['name','age']
this.taps = []; // 存放所有回调函数的数组
this.call = CALL_DELEGATE;
// 添加异步方法
this.callAsync = CALL_ASYNC_DELEGATE;
this.promise = PROMISE_DELEGATE;
}
tap(options, fn) {
this._tap('sync', options, fn);
}
tapAsync(options, fn) {
this._tap('async', options, fn);
}
_tap(type, options, fn) {
// 如果是字符串,需要转化为对象
if (typeof options === 'string') {
options = {name: options};
}
let tapInfo = {...options, type, fn};
this._insert(tapInfo);
}
_resetCompilation(){
this.call = CALL_DELEGATE;
}
_insert(tapInfo) {
// 每次需要重置call方法
this._resetCompilation();
this.taps.push(tapInfo);
}
compile(options) {
throw new Error("Abstract: should be overridden");
}
_createCall(type) {
return this.compile({
taps: this.taps,
args: this._args,
type
});
}
}
//创建一个call的代理方法
const CALL_DELEGATE = function(...args) {
//动态的创建call方法,并且赋给this.call
this.call = this._createCall("sync");
//执行动态创建出来的call方法
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);
};
module.exports = Hook;
- tapable\HookCodeFactory.js
class HookCodeFactory {
// 初始化
setup(hookInstance, options) {
// 映射出来一个回调函数的数组赋给 hooks实例的_x属性
// _x才是真正回调函数的数组
hookInstance._x = options.taps.map(item => item.fn);
}
init(options) {
this.options = options;
}
deinit() {
this.options = null;
}
// 参数处理
args(options = {}) {
let {before, after} = options;
let allArgs = this.options.args || []; //原始参数
if (before) allArgs = [before, ...allArgs];
if (after) allArgs = [...allArgs, after];
if (allArgs.length > 0) {
return allArgs.join(', ');
}
return '';
}
header() {
let code = "";
code += "var _x = this._x;\n";//_x是回调函数的数组
return code;
}
create(options) {
this.init(options);
let fn;
switch(this.options.type) {
case 'sync':
fn = new Function(
this.args(),
this.header() + this.content({
onDone: () => ''
})
);
break;
case 'async':
fn = new Function(
this.args({after:'_callback'}),
this.header()+this.content({
onDone:()=>"_callback();\n"
})
)
break;
case 'promise':
/* fn = new Function(this.args(),this.header()
+`return Promise.all(_x.map(item=>item(${this.args()})));`); */
let content = this.content({
onDone:()=>" _resolve();\n"
});
content = `return new Promise(function (_resolve, _reject) {
${content}
})`;
fn = new Function(
this.args(),
this.header() + content
)
break;
}
this.deinit();
return fn;
}
callTapsSeries({onDone}) {
if (this.options.taps.length === 0) {
return onDone();
}
let code = '';
let current = onDone;
for(let j = this.options.taps.length - 1; j >= 0; j--) {
const content = this.callTap(j, {onDone: current});
current = () => content;
}
code += current();
// 顺序需要调试一下,
// console.log(code);
return code;
}
callTapsParallel({onDone}) {
let code = `var _counter = ${this.options.taps.length};\n`;
if (onDone) {
code += `
var _done = function() {
${onDone()}
}
`;
}
for (let i = 0; i < this.options.taps.length; i++) {
const done = () => `if (--_counter === 0) _done()`;
code += this.callTap(i, {onDone: done});
}
return code;
}
callTap(tapIndex, {onDone}) {
let code = '';
code += `var _fn${tapIndex} = _x[${tapIndex}];`;
let tap = this.options.taps[tapIndex];
switch(tap.type) {
case 'sync' :
code += `_fn${tapIndex}(${this.args()});`
if (onDone) {
code += onDone();
}
break;
case 'async':
let cbCode = `
function (_err${tapIndex}) {
if (_err${tapIndex}) {
_callback(_err${tapIndex});
} else {
${onDone()}
}
}
`;
code += `_fn${tapIndex}(${this.args({after:cbCode})});`;
break;
case 'promise':
code = `
var _fn${tapIndex} = _x[${tapIndex}];
var _promise${tapIndex} = _fn${tapIndex}(${this.args()});
_promise${tapIndex}.then(
function () {
${onDone()}
}
);
`;
break;
default:
break;
}
return code;
}
}
module.exports = HookCodeFactory;
4 interceptor
- 所有钩子提供的额外的拦截器API,有三个:
- register 每添加一个tap的回调函数都会触发一次
- tap: 每个钩子执行之前(多个钩子执行多个),就会触发这个函数
- call 当你的钩子触发之前,(就是call()之前),就会触发这个函数,多个钩子执行一次
- Context(上下文) 插件和拦截器都可以选择加入一个可选的 context对象, 这个可以被用于传递随意的值到队列中的插件和拦截器
使用
const {SyncHook} = require('tapable');
const syncHook = new SyncHook(["name","age"]);
// 第一个拦截器
syncHook.intercept({
context: true, // 上下文对象
register: (tapinfo) => { // 当注册一个新的回调函数的时候调用一次
console.log(`${tapinfo.name}-1 注册`);
tapinfo.register1 = 'register1';
return tapinfo;
},
tap: (context, tapinfo) => { // 每个回调函数都会触发一次
console.log('开始触发1', context);
if (context) {
context.name1 = 'name1';
}
},
call: (context, name, age) => { // 每个call触发,所有的回调只会总共触发一次
console.log('开始调用1', context, name, age);
}
});
// 第二个拦截器
syncHook.intercept({
context: true, // 上下文对象
register: (tapinfo) => { // 当注册一个新的回调函数的时候调用一次
console.log(`${tapinfo.name}-2 注册`);
tapinfo.register2 = 'register2';
return tapinfo;
},
tap: (context, tapinfo) => { // 每个回调函数都会触发一次
console.log('开始触发2', context);
if (context) {
context.name2 = 'name2';
}
},
call: (context, name, age) => { // 每个call触发,所有的回调只会总共触发一次
console.log('开始调用2', context, name, age);
}
});
// 注册
syncHook.tap({name: 'tap1函数A', context: true}, (name, age) => {
console.log('回调1', name, age);
})
syncHook.tap({name: 'tap2函数B', context: true}, (name, age) => {
console.log('回调2', name, age);
})
// 执行
syncHook.call('test', 10);
// 执行结果
=register=
tap1函数A-1 注册
tap1函数A-2 注册
tap2函数B-1 注册
tap2函数B-2 注册
=call=
开始调用1 {} test 10
开始调用2 {} test 10
=tap=
开始触发1 {}
开始触发2 { name1: 'name1' }
回调1 { name1: 'name1', name2: 'name2' } test
开始触发1 { name1: 'name1', name2: 'name2' }
开始触发2 { name1: 'name1', name2: 'name2' }
回调2 { name1: 'name1', name2: 'name2' } test
// 需要构建的临时函数
(function anonymous(name, age) {
var _x = this._x;
var _taps = this.taps;
var _interceptors = this.interceptors;
_interceptors[0].call(name, age);
_interceptors[1].call(name, age);
var _tap0 = _taps[0];
_interceptors[0].tap(_tap0);
_interceptors[1].tap(_tap0);
var _fn0 = _x[0];
_fn0(name, age);
var _tap1 = _taps[1];
_interceptors[0].tap(_tap1);
_interceptors[1].tap(_tap1);
var _fn1 = _x[1];
_fn1(name, age);
});
实现
- tapable/Hook.js
class Hook {
constructor(args) {
if (!Array.isArray(args)) args = [];
this._args = args; // 存放着形参数组 ['name','age']
this.taps = []; // 存放所有回调函数的数组
+ this.interceptors = [];//存放着所有的拦截器
this.call = CALL_DELEGATE;
this.callAsync = CALL_ASYNC_DELEGATE;
this.promise = PROMISE_DELEGATE;
}
tap(options, fn) {
this._tap('sync', options, fn);
}
tapAsync(options, fn) {
this._tap('async', options, fn);
}
tapPromise(options, fn) {
this._tap('promise', options, fn);
}
_tap(type, options, fn) {
// 如果是字符串,需要转化为对象
if (typeof options === 'string') {
options = {name: options};
}
let tapInfo = {...options, type, fn};
// 执行拦截器
+ tapInfo=this._runRegisterInterceptors(tapInfo);
this._insert(tapInfo);
}
+ _runRegisterInterceptors(tapInfo){
+ for(const interceptor of this.interceptors){
+ if (interceptor.register) {
+ let newTapInfo = interceptor.register(tapInfo);
+ if (newTapInfo) {
+ tapInfo = newTapInfo;
+ }
+ }
+ }
+ return tapInfo;
+ }
// 注册拦截器
intercept(interceptor){
this.interceptors.push(interceptor);
}
_resetCompilation(){
this.call = CALL_DELEGATE;
}
_insert(tapInfo) {
// 每次需要重置call方法
this._resetCompilation();
this.taps.push(tapInfo);
}
compile(options) {
throw new Error("Abstract: should be overridden");
}
_createCall(type) {
return this.compile({
taps: this.taps,
args: this._args,
+ interceptors:this.interceptors,
type
});
}
}
//创建一个call的代理方法
const CALL_DELEGATE = function(...args) {
//动态的创建call方法,并且赋给this.call
this.call = this._createCall("sync");
//执行动态创建出来的call方法
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);
};
module.exports = Hook;
- tapable/HookCodeFactory.js
class HookCodeFactory {
// 初始化
setup(hookInstance, options) {
// 映射出来一个回调函数的数组赋给 hooks实例的_x属性
// _x才是真正回调函数的数组
hookInstance._x = options.taps.map(item => item.fn);
}
init(options) {
this.options = options;
}
deinit() {
this.options = null;
}
args(options = {}) {
let {before, after} = options;
let allArgs = this.options.args || []; //原始参数
if (before) allArgs = [before, ...allArgs];
if (after) allArgs = [...allArgs, after];
if (allArgs.length > 0) {
return allArgs.join(', ');
}
return '';
}
needContext(){
for(const tapInfo of this.options.taps){
if(tapInfo.context) return true;
}
}
header() {
let code = "";
code += "var _x = this._x;\n";//_x是回调函数的数组
// 拼接拦截器
+ if (this.needContext()) {
+ code += `var _context = {};\n`;
+ } else {
+ code += `var _context;\n`;
+ }
+ if (this.options.interceptors.length>0) {
+ code += `var _taps = this.taps;\n`;
+ code += `var _interceptors = this.interceptors;\n`;
+ }
+ for (let k = 0; k < this.options.interceptors.length; k++) {
+ const interceptor = this.options.interceptors[k];
+ if (interceptor.call)
+ code += `_interceptors[${k}].call(${this.args({
+ before: interceptor.context ? "_context" : undefined
+ })});\n`;
+ }
return code;
}
create(options) {
this.init(options);
let fn;
switch(this.options.type) {
case 'sync':
fn = new Function(
this.args(),
this.header() + this.content({
onDone: () => ''
})
);
break;
case 'async':
fn = new Function(
this.args({after:'_callback'}),
this.header()+this.content({
onDone:()=>"_callback();\n"
})
)
break
case 'promise':
/* fn = new Function(this.args(),this.header()
+`return Promise.all(_x.map(item=>item(${this.args()})));`); */
let content = this.content({
onDone:()=>" _resolve();\n"
});
content = `return new Promise(function (_resolve, _reject) {
${content}
})`;
fn = new Function(
this.args(),
this.header() + content
)
break;
}
this.deinit();
return fn;
}
callTapsSeries({onDone}) {
if (this.options.taps.length === 0) {
return onDone();
}
let code = '';
let current = onDone;
for(let j = this.options.taps.length - 1; j >= 0; j--) {
const content = this.callTap(j, {onDone: current});
current = () => content;
}
code += current();
// 顺序需要调试一下,
// console.log(code);
return code;
}
callTapsParallel({onDone}) {
let code = `var _counter = ${this.options.taps.length};\n`;
if (onDone) {
code += `
var _done = function() {
${onDone()}
}
`;
}
for (let i = 0; i < this.options.taps.length; i++) {
const done = () => `if (--_counter === 0) _done()`;
code += this.callTap(i, {onDone: done});
}
return code;
}
callTap(tapIndex, {onDone}) {
let code = '';
+ if (this.options.interceptors.length > 0) {
+ code += `var _tap${tapIndex} = _taps[${tapIndex}];`;
+ for (let i = 0; i < this.options.interceptors.length; i++) {
+ let interceptor = this.options.interceptors[i];
+ if(interceptor.tap){
+ code += `_interceptors[${i}].tap(${this.needContext() && '_context,'}_tap${tapIndex});`;
+ }
+ }
+ }
code += `var _fn${tapIndex} = _x[${tapIndex}];`;
let tap = this.options.taps[tapIndex];
switch(tap.type) {
case 'sync' :
code += `_fn${tapIndex}(${this.args()});`
if (onDone) {
code += onDone();
}
break;
case 'async':
let cbCode = `
function (_err${tapIndex}) {
if (_err${tapIndex}) {
_callback(_err${tapIndex});
} else {
${onDone()}
}
}
`;
code += `_fn${tapIndex}(${this.args({after:cbCode})});`;
break;
case 'promise':
code = `
var _fn${tapIndex} = _x[${tapIndex}];
var _promise${tapIndex} = _fn${tapIndex}(${this.args()});
_promise${tapIndex}.then(
function () {
${onDone()}
}
);
`;
break;
default:
break;
}
return code;
}
}
module.exports = HookCodeFactory;
5 HookMap
- 用对象存储钩子函数的帮助方法
const {SyncHook, HookMap} = require('tapable');
const keyedHookMap = new HookMap(() => new SyncHook(["name"]));
keyedHookMap.for('key1').tap('plugin1', (name) => {
console.log(1, name);
});
keyedHookMap.for('key2').tap('plugin2', (name) => {
console.log(2, name);
});
const hook1 = keyedHookMap.get('key1');
hook1.call('test1');
const hook2 = keyedHookMap.get('key2');
hook2.call('test2');
// 1 'test1'
// 2 'test2'
// 源码实现
class HookMap {
constructor(factory) {
this._map = new Map();
this._factory = factory;
}
get(key) {
return this._map.get(key);
}
for(key) {
const hook = this.get(key);
if (hook) return hook;
let newHook = this._factory();
this._map.set(key, newHook);
return newHook;
}
}
6 stage 和 before
- 用于提高钩子函数的优先执行顺序
- 如果同时存在stage和before,before优先
- 实现原理就是在放入钩子回调的时候,利用算法进行顺序调整
使用
// stage
const {SyncHook} = require('tapable');
let hook = new SyncHook(['name']);
hook.tap({name: 'tap1', stage:1}, (name) => {
console.log('tap1',name);
});
hook.tap({name: 'tap3', stage:3}, (name) => {
console.log('tap3',name);
});
hook.tap({name: 'tap4', stage:4}, (name) => {
console.log('tap4',name);
});
hook.tap({name: 'tap2', stage:2}, (name) => {
console.log('tap2',name);
});
hook.call('test');
/**
tap1 test
tap2 test
tap3 test
tap4 test
*/
// before
const {SyncHook} = require('tapable');
let hook = new SyncHook(['name']);
hook.tap({name: 'tap1', stage:1}, (name) => {
console.log('tap1',name);
});
hook.tap({name: 'tap3', stage:3}, (name) => {
console.log('tap3',name);
});
hook.tap({name: 'tap4', stage:4}, (name) => {
console.log('tap4',name);
});
hook.tap({name: 'tap2', stage:2, before:['tap1']}, (name) => {
console.log('tap2',name);
});
hook.call('test');
/**
tap2 test
tap1 test
tap3 test
tap4 test
*/
实现
// tapable/Hook.js
class Hook {
constructor(args) {
if (!Array.isArray(args)) args = [];
this._args = args; // 存放着形参数组 ['name','age']
this.taps = []; // 存放所有回调函数的数组
this.interceptors = [];//存放着所有的拦截器
this.call = CALL_DELEGATE;
this.callAsync = CALL_ASYNC_DELEGATE;
this.promise = PROMISE_DELEGATE;
}
tap(options, fn) {
this._tap('sync', options, fn);
}
tapAsync(options, fn) {
this._tap('async', options, fn);
}
tapPromise(options, fn) {
this._tap('promise', options, fn);
}
_tap(type, options, fn) {
// 如果是字符串,需要转化为对象
if (typeof options === 'string') {
options = {name: options};
}
let tapInfo = {...options, type, fn};
// 执行拦截器
tapInfo=this._runRegisterInterceptors(tapInfo);
this._insert(tapInfo);
}
_runRegisterInterceptors(tapInfo){
for(const interceptor of this.interceptors){
if (interceptor.register) {
let newTapInfo = interceptor.register(tapInfo);
if (newTapInfo) {
tapInfo = newTapInfo;
}
}
}
return tapInfo;
}
// 注册拦截器
intercept(interceptor){
this.interceptors.push(interceptor);
}
_resetCompilation(){
this.call = CALL_DELEGATE;
}
_insert(tapInfo) {
// 每次需要重置call方法
this._resetCompilation();
// 优先级处理
+ let before;
+ if (typeof tapInfo.before === 'string') {
+ before = new Set([tapInfo.before]);
+ } else if (Array.isArray(tapInfo.before)) {
+ before = new Set(tapInfo.before);
+ }
+ let stage = 0;
+ if (typeof tapInfo.stage === 'number') {
+ stage = tapInfo.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] = tapInfo;
}
compile(options) {
throw new Error("Abstract: should be overridden");
}
_createCall(type) {
return this.compile({
taps: this.taps,
args: this._args,
interceptors:this.interceptors,
type
});
}
}
//创建一个call的代理方法
const CALL_DELEGATE = function(...args) {
//动态的创建call方法,并且赋给this.call
this.call = this._createCall("sync");
//执行动态创建出来的call方法
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);
};
module.exports = Hook;