Webpack本質上是一種事件流的機制,它的工做流程就是將各個插件串聯起來,而實現這一切的核心就是Tapable,webpack中最核心的負責編譯的Compiler和負責建立bundle的Compilation都是Tapable的實例webpack
同步的鉤子(其實就是發佈訂閱)
web
串行同步執行,不關心返回值數組
class SyncHook{
constructor() {
this.tasks=[];
}
tap(name,task) {
this.tasks.push(task);
}
call() {
this.tasks.forEach(task=>task(...arguments));
}
}
let queue = new SyncHook(['name']);
queue.tap('1',function(name){
console.log(name,1);
});
queue.tap('2',function(name){
console.log(name,2);
});
queue.tap('3',function(name){
console.log(name,3);
});
queue.call('zfpx');
複製代碼
串行同步執行,有一個返回值不爲null則跳過剩下的邏輯,也就是隻要一個有返回值,下面的就不會執行了。promise
class SyncBailHook{
constructor() {
this.tasks=[];
}
tap(name,task) {
this.tasks.push(task);
}
call() {
let i=0,ret;
do {
ret=this.tasks[i++](...arguments);
} while (!ret);
}
}
let queue = new SyncBailHook(['name']);
queue.tap('1',function(name){
console.log(name,1);
return 'Wrong';
});
queue.tap('2',function(name){
console.log(name,2);
});
queue.tap('3',function(name){
console.log(name,3);
});
queue.call('zfpx');
複製代碼
上一個監聽函數的返回值能夠傳給下一個監聽函數(注意這裏的實現就是用到reduce)bash
class SyncWaterfallHook{
constructor() {
this.tasks=[];
}
tap(name,task) {
this.tasks.push(task);
}
call() {
let [first,...tasks]=this.tasks;
tasks.reduce((ret,task)=>task(ret),first(...arguments));
}
}
let queue = new SyncWaterfallHook(['name']);
queue.tap('1',function(name,age){
console.log(name,age,1);
return 1;
});
queue.tap('2',function(data){
console.log(data,2);
return 2;
});
queue.tap('3',function(data){
console.log(data,3);
});
queue.call('zfpx',9);
複製代碼
監聽函數返回true表示繼續循環,返回undefine表示結束循環異步
class SyncLoopHook{
constructor() {
this.tasks=[];
}
tap(name,task) {
this.tasks.push(task);
}
call(...args) {
this.tasks.forEach(task => {
let ret=true;
do {
ret = task(...args);
}while(ret == true || !(ret === undefined))
});
}
}
let queue = new SyncLoopHook(['name']);
let count = 0;
queue.tap('1',function(name){
console.log(count++);
if(count==3){
return;
}else{
return true;
}
});
queue.call('zfpx');
複製代碼
異步的鉤子
函數
異步並行執行鉤子oop
class AsyncParallelHook{
constructor() {
this.tasks=[];
}
tap(name,task) {
this.tasks.push(task);
}
callAsync() {
let args=Array.from(arguments); // 將類數組轉化爲數組
let callback=args.pop();
this.tasks.forEach(task => task(...args));
callback();
}
}
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('zfpx',err=>{
console.log(err);
console.timeEnd('cost');
});
複製代碼
異步執行須要傳遞一個callback,每次callback執行以後,從新計算計數器。當計數器大小和task長度一致時,觸發最終的回調函數。ui
class AsyncParallelHook{
constructor() {
this.tasks=[];
}
tapAsync(name,task) {
this.tasks.push(task);
}
callAsync() {
let args=Array.from(arguments);
let callback=args.pop();
let i=0,length = this.tasks.length;
function done(err) {
if (++i == length) {
callback(err);
}
}
this.tasks.forEach(task => {
task(...args,done);
});
}
}
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('zfpx',err=>{
console.log(err);
console.timeEnd('cost');
});
複製代碼
每一次tapPromise返回一個promise,使用promise.all保證全部的promise成功返回以後,在執行最後的回調函數。this
class AsyncParallelHook{
constructor() {
this.tasks=[];
}
tapPromise(name,task) {
this.tasks.push(task);
}
promise() {
let promises = this.tasks.map(task => task());
return Promise.all(promises);
}
}
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('zfpx').then(()=>{
console.timeEnd('cost');
})
複製代碼
帶保險的異步並行執行鉤子
只要有一個有返回值,直接執行最後的回調函數。
class AsyncParallelBailHook{
constructor() {
this.tasks=[];
}
tap(name,task) {
this.tasks.push(task);
}
callAsync() {
let args=Array.from(arguments);
let callback=args.pop();
for (let i=0;i<this.tasks.length;i++){
let ret=this.tasks[i](...args);
if (ret) {
return callback(ret);
}
}
}
}
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('zfpx',err=>{
console.log(err);
console.timeEnd('cost');
});
複製代碼
只要傳遞的回調函數callback有參數就執行最後的回調函數,不然當計數器和task長度相等的時候,再執行最後的回調函數。
class AsyncParallelBailHook{
constructor() {
this.tasks=[];
}
tapAsync(name,task) {
this.tasks.push(task);
}
callAsync() {
let args=Array.from(arguments);
let finalCallback=args.pop();
let count=0,total=this.tasks.length;
function done(err) {
if (err) {
return finalCallback(err);
} else {
if (++count == total) {
return finalCallback();
}
}
}
for (let i=0;i<total;i++){
let task=this.tasks[i];
task(...args,done);
}
}
}
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('zfpx',err=>{
console.log(err);
console.timeEnd('cost');
});
複製代碼
class AsyncParallelBailHook{
constructor() {
this.tasks=[];
}
tapPromise(name,task) {
this.tasks.push(task);
}
promise() {
let args=Array.from(arguments);
let promises = this.tasks.map(task => task(...arguments));
return Promise.all(promises);
}
}
let queue = new AsyncParallelBailHook(['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);
reject();
},2000)
});
});
queue.tapPromise('3',function(name){
return new Promise(function(resolve,reject){
setTimeout(function(){
console.log(3);
resolve();
},3000)
});
});
queue.promise('zfpx').then(()=>{
console.timeEnd('cost');
},err => {
console.error(err);
console.timeEnd('cost');
})
複製代碼
異步串行鉤子
let {AsyncSeriesHook} = require('tapable');
class AsyncSeriesHook{
constructor() {
this.tasks=[];
}
tap(name,task) {
this.tasks.push(task);
}
callAsync() {
for (let i=0;i<total;i++){
let task=this.tasks[i];
task(...args,done);
}
}
}
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('zfpx',err=>{
console.log(err);
console.timeEnd('cost');
});
複製代碼
class AsyncSeriesHook{
constructor() {
this.tasks=[];
}
tapAsync(name,task) {
this.tasks.push(task);
}
callAsync() {
let args = Array.from(arguments);
let finalCallback = args.pop();
let index = 0, length = this.tasks.length;
let next = () => {
let task = this.tasks[index++];
if (task) {
task(...args, next);
} else {
finalCallback();
}
}
next();
}
}
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('zfpx',err=>{
console.log(err);
console.timeEnd('cost');
});
複製代碼
第一個task成功以後,在執行下一個promise
class AsyncSeriesHook{
constructor() {
this.tasks=[];
}
tapPromise(name,task) {
this.tasks.push(task);
}
promise() {
//first是第一個函數, tasks是剩下的函數
let [first, ...tasks] = this.tasks;
return tasks.reduce((a, b) => {
return a.then(() => b());
}, first(...args));
}
}
let queue=new AsyncSeriesHook(['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){
setTimeout(function(){
console.log(2);
resolve();
},2000)
});
});
queue.tapPromise('3',function(name,callback){
return new Promise(function(resolve){
setTimeout(function(){
console.log(3);
resolve();
},3000)
});
});
queue.promise('zfpx').then(data=>{
console.log(data);
console.timeEnd('cost');
});
複製代碼
class AsyncSeriesWaterfallHook{
constructor() {
this.tasks=[];
}
tapAsync(name,task) {
this.tasks.push(task);
}
callAsync() {
let args=Array.from(arguments);
let callback=args.pop();
let i=0,size = this.tasks.length;
let next=(err,data) => {
if (err) return callback(err);
let task=this.tasks[i++];
if (task) {
if (i==0) {
task(...args,next);
} else {
task(data,next);
}
} else {
callback(err,data);
}
}
next();
}
}
let queue = new AsyncSeriesWaterfallHook(['name']);
console.time('cost');
queue.tapAsync('1',function(name,callback){
setTimeout(function(){
console.log(1);
callback(null,1);
},1000)
});
queue.tapAsync('2',function(data,callback){
setTimeout(function(){
console.log(2);
callback(null,2);
},2000)
});
queue.tapAsync('3',function(data,callback){
setTimeout(function(){
console.log(3);
callback(null,3);
},3000)
});
queue.callAsync('zfpx',(err,data)=>{
console.log(err,data);
console.timeEnd('cost');
});
複製代碼
class AsyncSeriesWaterfallHook {
constructor() {
this.tasks = [];
}
tapPromise(name, task) {
this.tasks.push(task);
}
promise(...args) {
//first是第一個函數, tasks是剩下的函數
let [first, ...tasks] = this.tasks;
return tasks.reduce((a, b) => {
return a.then((data) => b(data));
}, first(...args));
}
}
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('zfpx').then(err => {
console.timeEnd('cost');
});
複製代碼