tapable据说了好久,终于下定决心系统学习一下javascript
const {
SyncHook,
SyncBailHook,
SyncWaterfallHook,
SyncLoopHook,
AsyncParallelHook,
AsyncParallelBailHook,
AsyncSeriesHook,
AsyncSeriesBailHook,
AsyncSeriesWaterfallHook
} = require("tapable");
复制代码
好的,方法一共是上述这么多,第一眼看过去,懵逼树下你和我,因此咱们仍是一点点来,一个个的分析、学习和了解前端
先来个使用的例子,例如前端开发者须要掌握哪些技能?java
const {SyncHook}= require('tapable');
const FrontEnd = new SyncHook();
复制代码
ok,就是上面这两句,咱们建立了个FrontEnd前端开发node
FrontEnd.tap('webpack',()=>{
console.log("get webpack")
});
FrontEnd.tap('react',()=>{
console.log("get react")
});
复制代码
ok,上面的tap就是用来绑定事件的,为前端开发添加了两个技能react
FrontEnd.learn=()=>{
FrontEnd.call()
};
FrontEnd.learn();
复制代码
get webpack
get react
复制代码
能够看到,经过上面的调用,咱们的前端开发已经学会了react、webpackjquery
前面知道FrontEnd这个群体,须要学react、webpack,但落到我的角度,究竟哪个开发者掌握这些技能了呢?webpack
const {SyncHook}= require('tapable');
const FrontEnd = new SyncHook();
FrontEnd.tap('webpack',(name)=>{
console.log(name+" get webpack")
});
FrontEnd.tap('react',(name)=>{
console.log(name+" get react")
});
FrontEnd.start=(name)=>{
FrontEnd.call(name)
};
FrontEnd.start('xiaoming');
复制代码
修改前面的代码,添加参数,预期是输出xxx get reactweb
undefined get webpack
undefined get react
复制代码
最终结果是undefined,也就是参数没传进去redux
这是由于const FrontEnd = new SyncHook();
建立SyncHook的时候没有约定参数,只要为其添加参数便可,以下:api
const {SyncHook}= require('tapable');
const FrontEnd = new SyncHook(['name']);// 添加参数约定
FrontEnd.tap('webpack',(name)=>{
console.log(name+" get webpack")
});
FrontEnd.tap('react',(name)=>{
console.log(name+" get react")
});
FrontEnd.start=(name)=>{
FrontEnd.call(name)
};
FrontEnd.start('xiaoming');
复制代码
最终输出:
xiaoming get webpack
xiaoming get react
复制代码
SyncHook实现比较简单,就是最简单的订阅发布
class SyncHook {
constructor(limit = []){
this.limit= limit;
this.tasks = [];
}
tap(name,task){
this.tasks.push(task);
}
call(...args){
const param = args.slice(0,this.limit.length);
this.tasks.forEach(item=>item(...param));
}
}
复制代码
总结:原理比较简单,没有太多技术含量,主要就是一个同步的钩子函数
熔断机制,若是前一个事件return true
,则再也不执行下一个,仍是前面的例子:
const {SyncBailHook} =require('tapable');
const FrontEnd = new SyncBailHook(['name']);
FrontEnd.tap('webpack',(name)=>{
console.log(name+" get webpack ")
});
FrontEnd.tap('react',(name)=>{
console.log(name+" get react")
});
FrontEnd.start=(...args)=>{
FrontEnd.call(...args)
};
FrontEnd.start('xiaoming');
复制代码
此时,把函数从SyncHook换成SyncBailHook,执行的结果没有任何区别
but,思考一下,学习很容易会学不下去,因此修改一下咱们的例子:
const {SyncBailHook} =require('tapable');
const FrontEnd = new SyncBailHook(['name']);
FrontEnd.tap('webpack',(name)=>{
console.log(name+" get webpack ")
return '学不动了啊!';
});
FrontEnd.tap('react',(name)=>{
console.log(name+" get react")
});
FrontEnd.start=(...args)=>{
FrontEnd.call(...args)
};
FrontEnd.start('xiaoming');
复制代码
此时仅输出:
xiaoming get webpack
复制代码
后面的react没有执行
总结:
SyncBailHook也十分简单,仍是以前那个例子:
class SyncBailHook {
constructor(limit = []){
this.limit= limit;
this.tasks = [];
}
tap(name,task){
this.tasks.push(task);
}
call(...args){
const param = args.slice(0,this.limit.length);
this.tasks.some(item=>item(...param));// 只改了一行
}
}
复制代码
能够看到,和上面SyncHook十分类似,无非就是把执行函数forEach,换成some,由于some是阻塞式执行,当返回true,则不会执行后面的内容
仍是先来个使用的例子,例如前端,技能都是一个个学的,要学完webpack再学react,例如:
const {SyncWaterfallHook} = require('tapable');
const FrontEnd = new SyncWaterfallHook(['name']);
FrontEnd.tap('webpack',(name)=>{
console.log(name+" get webpack ")
return '学完webpack了,该学react了';
});
FrontEnd.tap('react',(name)=>{
console.log(name+" get react")
});
FrontEnd.start=(...args)=>{
FrontEnd.call(...args)
};
FrontEnd.start('xiaoming');
复制代码
此时输出:
xiaoming get webpack
学完webpack了,该学react了 get react
复制代码
class SyncWaterfallHook {
constructor(limit = []){
this.limit= limit;
this.tasks = [];
}
tap(name,task){
this.tasks.push(task);
}
call(...args){
const param = args.slice(0,this.limit.length);
const [first,...others] = this.tasks;
const ret = first(...param);
others.reduce((pre,next)=>{
return next(pre);
},ret)
}
}
复制代码
SyncWaterfallHook实现也比较简单
总结:SyncWaterfallHook主要仍是用于函数之间对结果存在依赖的场景
仍是前面的例子,若是一次学不懂一门技术,那就要多学几遍,例如:
const FrontEnd = new SyncLoopHook(['name']);
let num = 0;
FrontEnd.tap('webpack',(name)=>{
console.log(name+" get webpack ")
return ++num === 3?undefined:'再学一次';
});
FrontEnd.tap('react',(name)=>{
console.log(name+" get react")
});
FrontEnd.start=(...args)=>{
FrontEnd.call(...args)
};
FrontEnd.start('xiaoming');
复制代码
上面执行的结果是:
xiaoming get webpack
xiaoming get webpack
xiaoming get webpack
xiaoming get react
复制代码
总结:主要场景是同一任务,须要执行屡次
class SyncLoopHook {
constructor(limit = []){
this.limit= limit;
this.tasks = [];
}
tap(name,task){
this.tasks.push(task);
}
call(...args){
const param = args.slice(0,this.limit.length);
let index = 0;
while(index<this.tasks.length){
const result = this.tasks[index](...param);
if(result === undefined){
index++;
}
}
}
}
复制代码
也能够换doWhile来实现
class SyncLoopHook {
constructor(limit = []){
this.limit= limit;
this.tasks = [];
}
tap(name,task){
this.tasks.push(task);
}
call(...args){
const param = args.slice(0,this.limit.length);
this.tasks.forEach(task=>{
let ret;
do{
ret = task(...param);
}while(ret!=undefined)
})
}
}
复制代码
总结:SyncLoopHook这个使用场景相对较少,不过了解一下也好
前面了解的都是同步hook,更关键的是异步hook
举个例子,同窗小王说去学前端了,但你也不知道他何时学完,只有他学完告诉你,你才知道他学完了,例:
const {AsyncParallelHook} = require('tapable');
const FrontEnd = new AsyncParallelHook(['name']);
FrontEnd.tapAsync('webpack',(name,cb)=>{
setTimeout(() => {
console.log(name+" get webpack ")
cb();
}, 1000);
});
FrontEnd.tapAsync('react',(name,cb)=>{
setTimeout(() => {
console.log(name+" get react")
cb();
}, 1000);
});
FrontEnd.start=(...args)=>{
FrontEnd.callAsync(...args,()=>{
console.log("end");
})
};
FrontEnd.start('小王');
复制代码
最终输出:
小王 get webpack
小王 get react
end
复制代码
class AsyncParallelHook {
constructor(limit = []){
this.limit= limit;
this.tasks = [];
}
tapAsync(name,task){
this.tasks.push(task);
}
callAsync(...args){
const finalCallBack = args.pop();
const param = args.slice(0,this.limit.length);
let index = 0;
const done=()=>{
index++;
if(index === this.tasks.length){
finalCallBack();
}
}
this.tasks.forEach(item=>item(...param,done))
}
}
复制代码
总结:AsyncParallelHook解决的问题和promise.all相似,都是用于解决异步并行的问题
前面虽然用:AsyncParralleHook可以解决异步,但并无使用primise,也没有类promise的概念
const {AsyncParallelHook} = require('tapable');
const FrontEnd = new AsyncParallelHook(['name']);
FrontEnd.tapPromise('webpack',(name)=>{
return new Promise((resolve)=>{
setTimeout(() => {
console.log(name+" get webpack ")
resolve();
}, 1000);
})
});
FrontEnd.tapPromise('react',(name,cb)=>{
return new Promise((resolve)=>{
setTimeout(() => {
console.log(name+" get react ")
resolve();
}, 1000);
})
});
FrontEnd.start=(...args)=>{
FrontEnd.promise(...args).then(()=>{
console.log("end");
})
};
FrontEnd.start('小王');
复制代码
调用上面的api后,输出:
小王 get webpack
小王 get react
end
复制代码
总结:
class AsyncParallelHook {
constructor(limit = []){
this.limit= limit;
this.tasks = [];
}
tapPromise(name,task){
this.tasks.push(task);
}
promise(...args){
const param = args.slice(0,this.limit.length);
const tasks = this.tasks.map(task=>task(...param));
return Promise.all(tasks)
}
}
复制代码
AsyncParallelBailHook这个钩子和前面的钩子不太同样 按前面的例子来说:
这就是AsyncParallelBailHook处理的事情
const {AsyncParallelBailHook} = require('tapable');
const FrontEnd = new AsyncParallelBailHook(['name']);
FrontEnd.tapPromise('webpack',(name)=>{
return new Promise((resolve,reject)=>{
setTimeout(() => {
console.log(name+" get webpack ")
reject('小王学崩了!');
}, 1000);
})
});
FrontEnd.tapPromise('react',(name,cb)=>{
return new Promise((resolve)=>{
setTimeout(() => {
console.log(name+" get react ")
resolve();
}, 2000);
})
});
FrontEnd.start=(...args)=>{
FrontEnd.promise(...args).then(()=>{
console.log("end");
},(err)=>{
console.log("据说:",err)
})
};
FrontEnd.start('小王');
复制代码
上面代码执行结果是:
小王 get webpack
据说: 小王学崩了!
小王 get react
复制代码
再看一个例子:
const {AsyncParallelBailHook} = require('tapable');
const FrontEnd = new AsyncParallelBailHook(['name']);
FrontEnd.tapPromise('webpack',(name)=>{
return new Promise((resolve,reject)=>{
setTimeout(() => {
console.log(name+" get webpack ")
reject();
}, 1000);
})
});
FrontEnd.tapPromise('react',(name,cb)=>{
return new Promise((resolve)=>{
setTimeout(() => {
console.log(name+" get react ")
resolve();
}, 2000);
})
});
FrontEnd.start=(...args)=>{
FrontEnd.promise(...args).then(()=>{
console.log("end");
},(err)=>{
console.log("据说:",err)
})
};
FrontEnd.start('小王');
复制代码
和上面就改了1行,就是reject内容为空,此时输出:
小王 get webpack
小王 get react
end
复制代码
总结:
这个AsyncParallelBailHook真真烧脑了好一会
class AsyncParallelBailHook {
constructor(limit = []){
this.limit= limit;
this.tasks = [];
}
tapPromise(name,task){
this.tasks.push(task);
}
promise(...args){
const param = args.slice(0,this.limit.length);
const tasks = this.tasks.map(task=>{
return new Promise((resolve,reject)=>{
task(...param).then((data)=>{
resolve(data);
},(err)=>{
err? reject(err):resolve();
});
})
});
return Promise.all(tasks)
}
}
复制代码
前面讲的是异步并行,如今该说异步串行了,例如小王,学完webpack才去学的react,你也不知道他何时学完,但他学完一个就会告诉你一下,例:
const {AsyncSeriesHook} = require('tapable');
const FrontEnd = new AsyncSeriesHook(['name']);
console.time('webpack');
console.time('react');
FrontEnd.tapPromise('webpack',(name,cb)=>{
return new Promise((resolve,reject)=>{
setTimeout(() => {
console.log(name+" get webpack ")
console.timeEnd('webpack');
resolve();
}, 1000);
})
});
FrontEnd.tapPromise('react',(name,cb)=>{
return new Promise((resolve)=>{
setTimeout(() => {
console.log(name+" get react ")
console.timeEnd('react');
resolve();
}, 1000);
})
});
FrontEnd.start=(...args)=>{
FrontEnd.promise(...args).then(()=>{
console.log("end");
})
};
FrontEnd.start('小王');
复制代码
上面代码执行结果:
小王 get webpack
webpack: 1010.781ms
小王 get react
react: 2016.598ms
end
复制代码
总结:AsyncSeriesHook解决的问题是异步串行,例如node的os.cpus()有限,能够把任务分批次执行,这样对性能有保障
class AsyncSeriesHook {
constructor(limit = []){
this.limit= limit;
this.tasks = [];
}
tapPromise(name,task){
this.tasks.push(task);
}
promise(...args){
const param = args.slice(0,this.limit.length);
const [first,...others] = this.tasks;
return others.reduce((pre,next)=>{
return pre.then(()=>next(...param))
},first(...param))
}
}
复制代码
仍是前面的例子,若是小王学前端,学了webapck就完全放弃了,那后面的react也就不用学了
const {AsyncSeriesBailHook} = require('tapable');
const FrontEnd = new AsyncSeriesBailHook(['name']);
console.time('webpack');
console.time('react');
FrontEnd.tapPromise('webpack',(name,cb)=>{
return new Promise((resolve,reject)=>{
setTimeout(() => {
console.log(name+" get webpack ")
console.timeEnd('webpack');
reject('小王完全放弃了');
}, 1000);
})
});
FrontEnd.tapPromise('react',(name,cb)=>{
return new Promise((resolve)=>{
setTimeout(() => {
console.log(name+" get react ")
console.timeEnd('react');
resolve();
}, 1000);
})
});
FrontEnd.start=(...args)=>{
FrontEnd.promise(...args).then(()=>{
console.log("end");
}).catch((err)=>{
console.log("err",err)
})
};
FrontEnd.start('小王');
复制代码
上面代码输出:
小王 get webpack
webpack: 1010.518ms
err 小王完全放弃了
复制代码
场景:主要是异步串行,若是某一个任务执行的结果reject或者return,那么后面的都将再也不执行
class AsyncSeriesBailHook {
constructor(limit = []){
this.limit= limit;
this.tasks = [];
}
tapPromise(name,task){
this.tasks.push(task);
}
promise(...args){
const param = args.slice(0,this.limit.length);
const [first,...others] = this.tasks;
return new Promise((resolve,reject)=>{
others.reduce((pre,next,index,arr)=>{
return pre
.then(()=>next(...param))
.catch((err=>{
arr.splice(index,arr.length-index);
reject(err);
})).then(()=>{
(index+1 === arr.length) && resolve();
})
},first(...param))
})
}
}
复制代码
AsyncSeriesBailHook实现难度要高不少
SyncWaterFallHook前面已经了解过了,就是前一个执行完的结果会传递给下一个执行函数,和AsyncSeriesWaterfallHook的区别就是,一个是同步一个是异步
具体来讲,例如只有一本教材,小王学完,小张才能学
const FrontEnd = new AsyncSeriesWaterfallHook(['name']);
FrontEnd.tapAsync('webpack',(name,cb)=>{
setTimeout(() => {
console.log(name+" get webpack ")
cb(null,'小李');
}, 1000);
});
FrontEnd.tapAsync('webpack',(name,cb)=>{
setTimeout(() => {
console.log(name+" get webpack ")
cb(null,'小张');
}, 1000);
});
FrontEnd.tapAsync('webpack',(name,cb)=>{
setTimeout(() => {
console.log(name+" get webpack ")
cb(null,'小红');
}, 1000);
});
FrontEnd.start=(...args)=>{
FrontEnd.callAsync(...args,(data)=>{
console.log("全学完了",)
})
};
FrontEnd.start('小王');
复制代码
上面代码,最终输出:
小王 get webpack
小李 get webpack
小张 get webpack
全学完了
复制代码
总结:这个的用法和SyncWaterFallHook的用法一致
class AsyncSeriesWaterfallHook {
constructor(limit = []){
this.limit= limit;
this.tasks = [];
}
tapAsync(name,task){
this.tasks.push(task);
}
callAsync(...args){
const param = args.slice(0,this.limit.length);
const finalCallBack = args.pop();
let index = 0;
const next = (err,data)=>{
const task = this.tasks[index];
if(!task)return finalCallBack();
if(index === 0){
task(...param,next)
}else{
task(data,next)
}
index++;
}
next();
}
}
复制代码
prmise版本的实现以下:
class AsyncSeriesWaterfallHook {
constructor(limit = []){
this.limit= limit;
this.tasks = [];
}
tapPromise(name,task){
this.tasks.push(task);
}
promise(...args){
const param = args.slice(0,this.limit.length);
const [first,...others] = this.tasks;
return others.reduce((pre,next)=>{
return pre.then((data)=>{
return data?next(data):next(...param);
})
},first(...param))
}
}
复制代码