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))
}
}
複製代碼