const {
SyncHook,
SyncBailHook,
SyncWaterfallHook,
SyncLoopHook,
AsyncParallelHook,
AsyncParallelBailHook,
AsyncSeriesHook,
AsyncSeriesBailHook,
AsyncSeriesWaterfallHook,
} = require("tapable");
同步Sync
和异步Async
,异步又分为并行
和串行
result !== undefined
则返回,不再继续执行。有:SyncBailHook、AsyncSeriesBailHook, AsyncParallelBailHookresult !== undefined
,则 result 会作为后一个事件函数的第一个参数,有 SyncWaterfallHook,AsyncSeriesWaterfallHookresult === undefined
,有 SyncLoopHook 和 AsyncSeriesLoopHookconst { SyncHook } = require("tapable");
const hook = new SyncHook(["name", "age"]);
hook.tap("1", (name, age) => {
console.log(1, name, age);
return 1;
});
hook.tap("2", (name, age) => {
console.log(2, name, age);
return 2;
});
hook.tap("3", (name, age) => {
console.log(3, name, age);
return 3;
});
hook.call("zhufeng", 10);
1 zhufeng 10
2 zhufeng 10
3 zhufeng 10
const { SyncBailHook } = require("tapable");
const hook = new SyncBailHook(["name", "age"]);
hook.tap("1", (name, age) => {
console.log(1, name, age);
//return 1;
});
hook.tap("2", (name, age) => {
console.log(2, name, age);
return 2;
});
hook.tap("3", (name, age) => {
console.log(3, name, age);
return 3;
});
hook.call("zhufeng", 10);
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);
return;
});
hook.tap("3", (name, age) => {
console.log(3, name, age);
return 3;
});
hook.call("zhufeng", 10);
const { SyncLoopHook } = require("tapable");
//当回调函数返回非undefined值的时候会停止调用后续的回调
let 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("zhufeng", 10);
//一共15次 12120 12121 12123
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
let { AsyncParallelHook } = require("tapable");
let queue = new AsyncParallelHook(["name"]);
console.time("cost");
queue.tap("1", function (name) {
console.log(1);
});
queue.tap("2", function (name) {
console.log(2);
});
queue.tap("3", function (name) {
console.log(3);
});
queue.callAsync("zhufeng", (err) => {
console.log(err);
console.timeEnd("cost");
});
let { AsyncParallelHook } = require("tapable");
let queue = new AsyncParallelHook(["name"]);
console.time("cost");
queue.tapAsync("1", function (name, callback) {
setTimeout(function () {
console.log(1);
callback();
}, 1000);
});
queue.tapAsync("2", function (name, callback) {
setTimeout(function () {
console.log(2);
callback();
}, 2000);
});
queue.tapAsync("3", function (name, callback) {
setTimeout(function () {
console.log(3);
callback();
}, 3000);
});
queue.callAsync("zhufeng", (err) => {
console.log(err);
console.timeEnd("cost");
});
let { AsyncParallelHook } = require("tapable");
let queue = new AsyncParallelHook(["name"]);
console.time("cost");
queue.tapPromise("1", function (name) {
return new Promise(function (resolve, reject) {
setTimeout(function () {
console.log(1);
resolve();
}, 1000);
});
});
queue.tapPromise("2", function (name) {
return new Promise(function (resolve, reject) {
setTimeout(function () {
console.log(2);
resolve();
}, 2000);
});
});
queue.tapPromise("3", function (name) {
return new Promise(function (resolve, reject) {
setTimeout(function () {
console.log(3);
resolve();
}, 3000);
});
});
queue.promise("zhufeng").then(() => {
console.timeEnd("cost");
});
let { AsyncParallelBailHook } = require("tapable");
let queue = new AsyncParallelBailHook(["name"]);
console.time("cost");
queue.tap("1", function (name) {
console.log(1);
return "Wrong";
});
queue.tap("2", function (name) {
console.log(2);
});
queue.tap("3", function (name) {
console.log(3);
});
queue.callAsync("zhufeng", (err) => {
console.log(err);
console.timeEnd("cost");
});
let { AsyncParallelBailHook } = require("tapable");
let queue = new AsyncParallelBailHook(["name"]);
console.time("cost");
queue.tapAsync("1", function (name, callback) {
console.log(1);
callback("Wrong");
});
queue.tapAsync("2", function (name, callback) {
console.log(2);
callback();
});
queue.tapAsync("3", function (name, callback) {
console.log(3);
callback();
});
queue.callAsync("zhufeng", (err) => {
console.log(err);
console.timeEnd("cost");
});
let { AsyncParallelBailHook } = require("tapable");
let queue = new AsyncParallelBailHook(["name"]);
console.time("cost");
queue.tapPromise("1", function (name) {
return new Promise(function (resolve, reject) {
setTimeout(function () {
console.log(1);
//对于promise来说,resolve还reject并没有区别
//区别在于你是否传给它们的参数
resolve(1);
}, 1000);
});
});
queue.tapPromise("2", function (name) {
return new Promise(function (resolve, reject) {
setTimeout(function () {
console.log(2);
resolve();
}, 2000);
});
});
queue.tapPromise("3", function (name) {
return new Promise(function (resolve, reject) {
setTimeout(function () {
console.log(3);
resolve();
}, 3000);
});
});
queue.promise("zhufeng").then(
(result) => {
console.log("成功", result);
console.timeEnd("cost");
},
(err) => {
console.error("失败", err);
console.timeEnd("cost");
}
);
let { AsyncSeriesHook } = require("tapable");
let queue = new AsyncSeriesHook(["name"]);
console.time("cost");
queue.tap("1", function (name) {
console.log(1);
});
queue.tap("2", function (name) {
console.log(2);
});
queue.tap("3", function (name) {
console.log(3);
});
queue.callAsync("zhufeng", (err) => {
console.log(err);
console.timeEnd("cost");
});
let { AsyncSeriesHook } = require("tapable");
let queue = new AsyncSeriesHook(["name"]);
console.time("cost");
queue.tapAsync("1", function (name, callback) {
setTimeout(function () {
console.log(1);
}, 1000);
});
queue.tapAsync("2", function (name, callback) {
setTimeout(function () {
console.log(2);
callback();
}, 2000);
});
queue.tapAsync("3", function (name, callback) {
setTimeout(function () {
console.log(3);
callback();
}, 3000);
});
queue.callAsync("zhufeng", (err) => {
console.log(err);
console.timeEnd("cost");
});
let { AsyncSeriesHook } = require("tapable");
let queue = new AsyncSeriesHook(["name"]);
console.time("cost");
queue.tapPromise("1", function (name) {
return new Promise(function (resolve) {
setTimeout(function () {
console.log(1, name);
resolve();
}, 1000);
});
});
queue.tapPromise("2", function (name) {
return new Promise(function (resolve) {
setTimeout(function () {
console.log(2, name);
resolve();
}, 2000);
});
});
queue.tapPromise("3", function (name) {
return new Promise(function (resolve) {
setTimeout(function () {
console.log(3, name);
resolve();
}, 3000);
});
});
queue.promise("zhufeng").then((data) => {
console.log(data);
console.timeEnd("cost");
});
let { AsyncSeriesBailHook } = require("tapable");
let queue = new AsyncSeriesBailHook(["name"]);
console.time("cost");
queue.tap("1", function (name) {
console.log(1);
return "Wrong";
});
queue.tap("2", function (name) {
console.log(2);
});
queue.tap("3", function (name) {
console.log(3);
});
queue.callAsync("zhufeng", (err) => {
console.log(err);
console.timeEnd("cost");
});
let { AsyncSeriesBailHook } = require("tapable");
let queue = new AsyncSeriesBailHook(["name"]);
console.time("cost");
queue.tapAsync("1", function (name, callback) {
setTimeout(function () {
console.log(1);
callback("wrong");
}, 1000);
});
queue.tapAsync("2", function (name, callback) {
setTimeout(function () {
console.log(2);
callback();
}, 2000);
});
queue.tapAsync("3", function (name, callback) {
setTimeout(function () {
console.log(3);
callback();
}, 3000);
});
queue.callAsync("zhufeng", (err) => {
console.log(err);
console.timeEnd("cost");
});
let { AsyncSeriesBailHook } = require("tapable");
let queue = new AsyncSeriesBailHook(["name"]);
console.time("cost");
queue.tapPromise("1", function (name) {
return new Promise(function (resolve) {
setTimeout(function () {
console.log(1);
resolve();
}, 1000);
});
});
queue.tapPromise("2", function (name, callback) {
return new Promise(function (resolve, reject) {
setTimeout(function () {
console.log(2);
reject("失败了");
}, 2000);
});
});
queue.tapPromise("3", function (name, callback) {
return new Promise(function (resolve) {
setTimeout(function () {
console.log(3);
resolve();
}, 3000);
});
});
queue.promise("zhufeng").then(
(data) => {
console.log(data);
console.timeEnd("cost");
},
(error) => {
console.log(error);
console.timeEnd("cost");
}
);
let { AsyncSeriesWaterfallHook } = require("tapable");
let queue = new AsyncSeriesWaterfallHook(["name", "age"]);
console.time("cost");
queue.tap("1", function (name, age) {
console.log(1, name, age);
return "return1";
});
queue.tap("2", function (data, age) {
console.log(2, data, age);
return "return2";
});
queue.tap("3", function (data, age) {
console.log(3, data, age);
});
queue.callAsync("zhufeng", 10, (err) => {
console.log(err);
console.timeEnd("cost");
});
let { AsyncSeriesWaterfallHook } = require("tapable");
let queue = new AsyncSeriesWaterfallHook(["name", "age"]);
console.time("cost");
queue.tapAsync("1", function (name, age, callback) {
setTimeout(function () {
console.log(1, name, age);
callback(null, 1);
}, 1000);
});
queue.tapAsync("2", function (data, age, callback) {
setTimeout(function () {
console.log(2, data, age);
callback(null, 2);
}, 2000);
});
queue.tapAsync("3", function (data, age, callback) {
setTimeout(function () {
console.log(3, data, age);
callback(null, 3);
}, 3000);
});
queue.callAsync("zhufeng", 10, (err, data) => {
console.log(err, data);
console.timeEnd("cost");
});
let { AsyncSeriesWaterfallHook } = require("tapable");
let queue = new AsyncSeriesWaterfallHook(["name"]);
console.time("cost");
queue.tapPromise("1", function (name) {
return new Promise(function (resolve) {
setTimeout(function () {
console.log(name, 1);
resolve(1);
}, 1000);
});
});
queue.tapPromise("2", function (data) {
return new Promise(function (resolve) {
setTimeout(function () {
console.log(data, 2);
resolve(2);
}, 2000);
});
});
queue.tapPromise("3", function (data) {
return new Promise(function (resolve) {
setTimeout(function () {
console.log(data, 3);
resolve(3);
}, 3000);
});
});
queue.promise("zhufeng").then((err) => {
console.timeEnd("cost");
});
const { SyncHook } = require("./tapable");
let syncHook = new SyncHook(["name", "age"]);
let fn1 = (name, age) => {
console.log(1, name, age);
}
syncHook.tap({name:'1'},fn1 );
let fn2 = (name, age) => {
console.log(2, name, age);
}
syncHook.tap("2",fn2);
syncHook.call("zhufeng", 10);
/**
(function anonymous(name, age) {
var _x = this._x;
var _fn0 = _x[0];
_fn0(name, age);
var _fn1 = _x[1];
_fn1(name, age);
})
*/
tapable\index.js
let SyncHook = require('./SyncHook');
module.exports = {
SyncHook
}
tapable\Hook.js
class Hook{
constructor(args){
if(!Array.isArray(args)) args=[];
this.args = args;
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){
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
});
}
}
const CALL_DELEGATE = function(...args) {
this.call = this._createCall("sync");
return this.call(...args);
};
module.exports = Hook;
tapable\SyncHook.js
let Hook = require('./Hook');
const HookCodeFactory = require('./HookCodeFactory');
class SyncHookCodeFactory extends HookCodeFactory{
content(){
return this.callTapsSeries()
}
}
let factory = new SyncHookCodeFactory();
class SyncHook extends Hook{
compile(options) {
factory.setup(this,options);
return factory.create(options);
}
}
module.exports = SyncHook;
HookCodeFactory.js
class HookCodeFactory {
setup(hookInstance, options) {
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";
return code;
}
create(options) {
this.init(options);
let fn;
switch (this.options.type) {
case 'sync':
fn = new Function(
this.args(),
this.header() + this.content()
)
break;
default:
break;
}
this.deinit();
return fn;
}
callTapsSeries() {
if (this.options.taps.length === 0) {
return '';
}
let code = "";
for (let j =0;j< this.options.taps.length ; j++) {
const content = this.callTap(j);
code += content;
}
return code;
}
callTap(tapIndex) {
let code = "";
code += `var _fn${tapIndex} = _x[${tapIndex}];\n`
let tap = this.options.taps[tapIndex];
switch (tap.type) {
case 'sync':
code += `_fn${tapIndex}(${this.args()});\n`;
break;
default:
break;
}
return code;
}
}
module.exports = HookCodeFactory;
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);
});
debugger
hook.callAsync('zhufeng', 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 (--_counter === 0) _done();
});
var _fn1 = _x[1];
_fn1(name, age, function (_err1) {
if (--_counter === 0) _done();
});
var _fn2 = _x[2];
_fn2(name, age, function (_err2) {
if (--_counter === 0) _done();
});
});
*/
tapable\index.js
let SyncHook = require('./SyncHook');
+let AsyncParallelHook = require('./AsyncParallelHook');
module.exports = {
SyncHook,
+ AsyncParallelHook
}
tapable\AsyncParallelHook.js
let Hook = require('./Hook');
const HookCodeFactory = require('./HookCodeFactory');
class AsyncParallelHookCodeFactory extends HookCodeFactory{
content(){
return this.callTapsParallel()
}
}
let factory = new AsyncParallelHookCodeFactory();
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;
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){
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
});
}
}
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);
+};
module.exports = Hook;
tapable\HookCodeFactory.js
class HookCodeFactory {
setup(hookInstance, options) {
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";
return code;
}
create(options) {
this.init(options);
let fn;
switch (this.options.type) {
case 'sync':
fn = new Function(
this.args(),
this.header() + this.content()
)
break;
+ case 'async':
+ fn = new Function(
+ this.args({after:'_callback'}),
+ this.header()+this.content()
+ )
+ break;
default:
break;
}
this.deinit();
return fn;
}
+ callTapsParallel(){
+ let code = `var _counter = ${this.options.taps.length};\n`;
+ code+=`
+ var _done = function () {
+ _callback();
+ };
+ `;
+ for (let j =0;j< this.options.taps.length ; j++) {
+ const content = this.callTap(j);
+ code += content;
+ }
+ return code;
+ }
callTapsSeries() {
if (this.options.taps.length === 0) {
return '';
}
let code = "";
for (let j =0;j< this.options.taps.length ; j++) {
const content = this.callTap(j);
code += content;
}
return code;
}
callTap(tapIndex) {
let code = "";
code += `var _fn${tapIndex} = _x[${tapIndex}];\n`
let tap = this.options.taps[tapIndex];
switch (tap.type) {
case 'sync':
code += `_fn${tapIndex}(${this.args()});\n`;
break;
+ case 'async':
+ code += `
+ _fn${tapIndex}(${this.args({after:`function (_err${tapIndex}) {
+ if (--_counter === 0) _done();
+ }`})});
+ `;
+ break;
default:
break;
}
return code;
}
}
module.exports = HookCodeFactory;
//let { AsyncParallelHook } = require("tapable");
let { AsyncParallelHook } = require("./tapable2");
let queue = new AsyncParallelHook(["name", "age"]);
console.time("cost");
queue.tapPromise("1", function (name, age) {
return new Promise(function (resolve) {
setTimeout(function () {
console.log(1, name, age);
resolve();
}, 1000);
});
});
queue.tapPromise("2", function (name, age) {
return new Promise(function (resolve) {
setTimeout(function () {
console.log(2, name, age);
resolve();
}, 2000);
});
});
queue.tapPromise("3", function (name, age) {
return new Promise(function (resolve) {
setTimeout(function () {
console.log(3, name, age);
resolve();
}, 3000);
});
});
queue.promise("zhufeng", 10).then(
(result) => {
console.timeEnd("cost");
},
(error) => {
console.log(error);
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\Hook.js
class Hook{
constructor(args){
if(!Array.isArray(args)) args=[];
this.args = args;
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);
}
+ tapPromise(options,fn){
+ this._tap("promise", 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){
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
});
}
}
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);
+};
module.exports = Hook;
tapable\AsyncParallelHook.js
let Hook = require('./Hook');
const HookCodeFactory = require('./HookCodeFactory');
class AsyncParallelHookCodeFactory extends HookCodeFactory{
+ content({onDone}){
+ return this.callTapsParallel({onDone})
}
}
let factory = new AsyncParallelHookCodeFactory();
class AsyncParallelHook extends Hook{
compile(options) {
factory.setup(this,options);
return factory.create(options);
}
}
module.exports = AsyncParallelHook;
tapable\HookCodeFactory.js
class HookCodeFactory {
setup(hookInstance, options) {
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";
return code;
}
create(options) {
this.init(options);
let fn;
switch (this.options.type) {
case 'sync':
fn = new Function(
this.args(),
this.header() + this.content()
)
break;
case 'async':
fn = new Function(
this.args({after:'_callback'}),
+ this.header()+this.content({ onDone:()=>" _callback();\n"})
)
break;
+ case 'promise':
+ let tapsContent = this.content({ onDone:()=>" _resolve();\n"});
+ let content = `return new Promise(function (_resolve, _reject) {
+ ${tapsContent}
+ })`;
+ fn = new Function(
+ this.args(),
+ this.header()+content
+ )
+ break;
default:
break;
}
this.deinit();
return fn;
}
+ callTapsParallel({onDone}){
let code = `var _counter = ${this.options.taps.length};\n`;
code+=`
var _done = function () {
+ ${onDone()}
};
`;
for (let j =0;j< this.options.taps.length ; j++) {
const content = this.callTap(j);
code += content;
}
return code;
}
callTapsSeries() {
if (this.options.taps.length === 0) {
return '';
}
let code = "";
for (let j =0;j< this.options.taps.length ; j++) {
const content = this.callTap(j);
code += content;
}
return code;
}
callTap(tapIndex) {
let code = "";
code += `var _fn${tapIndex} = _x[${tapIndex}];\n`
let tap = this.options.taps[tapIndex];
switch (tap.type) {
case 'sync':
code += `_fn${tapIndex}(${this.args()});\n`;
break;
case 'async':
code += `
_fn${tapIndex}(${this.args({after:`function (_err${tapIndex}) {
if (--_counter === 0) _done();
}`})});
`;
break;
+ case 'promise':
+ code = `
+ var _fn${tapIndex} = _x[${tapIndex}];
+ var _promise${tapIndex} = _fn${tapIndex}(${this.args()});
+ _promise${tapIndex}.then(
+ function () {
+ if (--_counter === 0) _done();
+ }
+ );
+ `;
default:
break;
}
return code;
}
}
module.exports = HookCodeFactory;
const {SyncHook} = require('tapable');
const syncHook = new SyncHook(["name","age"]);
syncHook.intercept({
register:(tapInfo)=>{//当你新注册一个回调函数的时候触发
console.log(`拦截器1开始register`);
return tapInfo;
},
tap:(tapInfo)=>{//每个回调函数都会触发一次
console.log(`拦截器1开始tap`);
},
call:(name,age)=>{//每个call触发,所有的回调只会总共触发一次
console.log(`拦截器1开始call`,name,age);
}
});
syncHook.intercept({
register:(tapInfo)=>{//当你新注册一个回调函数的时候触发
console.log(`拦截器2开始register`);
return tapInfo;
},
tap:(tapInfo)=>{//每个回调函数都会触发一次
console.log(`拦截器2开始tap`);
},
call:(name,age)=>{//每个call触发,所有的回调只会总共触发一次
console.log(`拦截器2开始call`,name,age);
}
});
syncHook.tap({name:'回调函数A'},(name,age)=>{
console.log(`回调A`,name,age);
});
//console.log(syncHook.taps[0]);
syncHook.tap({name:'回调函数B'},(name,age)=>{
console.log('回调B',name,age);
});
debugger
syncHook.call('zhufeng',10);
/**
拦截器1开始register
拦截器2开始register
拦截器1开始register
拦截器2开始register
拦截器1开始call zhufeng 10
拦截器2开始call zhufeng 10
拦截器1开始tap
拦截器2开始tap
回调A zhufeng 10
拦截器1开始tap
拦截器2开始tap
回调B zhufeng 10
*/
(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;
this.taps = [];
this.call = CALL_DELEGATE;
this.callAsync = CALL_ASYNC_DELEGATE;
this.promise = PROMISE_DELEGATE;
+ this.interceptors = [];
}
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){
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
});
}
}
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);
};
module.exports = Hook;
tapable\HookCodeFactory.js
class HookCodeFactory {
setup(hookInstance, options) {
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";
+ 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()});\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()
)
break;
case 'async':
fn = new Function(
this.args({after:'_callback'}),
this.header()+this.content({ onDone:()=>" _callback();\n"})
)
break;
case 'promise':
let tapsContent = this.content({ onDone:()=>" _resolve();\n"});
let content = `return new Promise(function (_resolve, _reject) {
${tapsContent}
})`;
fn = new Function(
this.args(),
this.header()+content
)
break;
default:
break;
}
this.deinit();
return fn;
}
callTapsParallel({onDone}){
let code = `var _counter = ${this.options.taps.length};\n`;
code+=`
var _done = function () {
${onDone()}
};
`;
for (let j =0;j< this.options.taps.length ; j++) {
const content = this.callTap(j);
code += content;
}
return code;
}
callTapsSeries() {
if (this.options.taps.length === 0) {
return '';
}
let code = "";
for (let j =0;j< this.options.taps.length ; j++) {
const content = this.callTap(j);
code += content;
}
return code;
}
callTap(tapIndex) {
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(_tap${tapIndex});`;
+ }
+ }
+ }
code += `var _fn${tapIndex} = _x[${tapIndex}];\n`
let tap = this.options.taps[tapIndex];
switch (tap.type) {
case 'sync':
code += `_fn${tapIndex}(${this.args()});\n`;
break;
case 'async':
code += `
_fn${tapIndex}(${this.args({after:`function (_err${tapIndex}) {
if (--_counter === 0) _done();
}`})});
`;
break;
case 'promise':
code = `
var _fn${tapIndex} = _x[${tapIndex}];
var _promise${tapIndex} = _fn${tapIndex}(${this.args()});
_promise${tapIndex}.then(
function () {
if (--_counter === 0) _done();
}
);
`;
default:
break;
}
return code;
}
}
module.exports = HookCodeFactory;
let {SyncHook,HookMap} = require('./tapable');
const keyedHookMap = new HookMap(()=>new SyncHook(["name"]));
keyedHookMap.for('key1').tap('plugin1',(name)=>{console.log(1,name);});
keyedHookMap.for('key1').tap('plugin2',(name)=>{console.log(2,name);});
const hook1 = keyedHookMap.get('key1');
hook1.call('zhufeng');
tapable\index.js
let SyncHook = require('./SyncHook');
let AsyncParallelHook = require('./AsyncParallelHook');
+let HookMap = require('./HookMap');
module.exports = {
SyncHook,
AsyncParallelHook,
+ HookMap
}
class HookMap {
constructor(factory) {
this._map = new Map();
this._factory = factory;
}
get(key) {
return this._map.get(key);
}
tapAsync(key, options, fn) {
return this.for(key).tapAsync(options, fn);
}
tapPromise(key, options, fn) {
return this.for(key).tapPromise(options, fn);
}
for(key) {
const hook = this.get(key);
if (hook) return hook;
let newHook = this._factory();
this._map.set(key, newHook);
return newHook;
}
}
module.exports = HookMap;
let {SyncHook} = require('tapable');
let hook = new SyncHook(['name']);
debugger
hook.tap({name:'tap1',stage:1},(name)=>{
console.log(1,name);
});
hook.tap({name:'tap3',stage:3},(name)=>{
console.log(3,name);
});
hook.tap({name:'tap5',stage:5},(name)=>{
console.log(4,name);
});
hook.tap({name:'tap2',stage:2},(name)=>{
console.log(2,name);
});
hook.call('zhufeng');
tapable\Hook.js
class Hook{
constructor(args){
if(!Array.isArray(args)) args=[];
this.args = args;
this.taps = [];
this.call = CALL_DELEGATE;
this.callAsync = CALL_ASYNC_DELEGATE;
this.promise = PROMISE_DELEGATE;
this.interceptors = [];
}
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){
this._resetCompilation();
+ 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 (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
});
}
}
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);
};
module.exports = Hook;
let {SyncHook} = require('tapable');
let hook = new SyncHook(['name']);
debugger
hook.tap({name:'tap1'},(name)=>{
console.log(1,name);
});
hook.tap({name:'tap3'},(name)=>{
console.log(3,name);
});
hook.tap({name:'tap5'},(name)=>{
console.log(4,name);
});
hook.tap({name:'tap2',before:['tap3','tap5']},(name)=>{
console.log(2,name);
});
hook.call('zhufeng');
class Hook{
constructor(args){
if(!Array.isArray(args)) args=[];
this.args = args;
this.taps = [];
this.call = CALL_DELEGATE;
this.callAsync = CALL_ASYNC_DELEGATE;
this.promise = PROMISE_DELEGATE;
this.interceptors = [];
}
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){
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
});
}
}
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);
};
module.exports = Hook;