摘要:接上一篇文章深入理解核心模塊鉤子同步版中三個(gè)注冊(cè)方法同步注冊(cè)的是中對(duì)三個(gè)觸發(fā)方法這一章節(jié)我們將分別實(shí)現(xiàn)異步的版本和版本異步鉤子的版本的版本的版本異步的鉤子分為并行和串行的鉤子,并行是指等待所有并發(fā)的異步事件執(zhí)行之后再執(zhí)行最終的異步回調(diào)。
接上一篇文章 深入理解Webpack核心模塊WTApable鉤子(同步版)
tapable中三個(gè)注冊(cè)方法
1 tap(同步) 2 tapAsync(cb) 3 tapPromise(注冊(cè)的是Promise)
tapable中對(duì)三個(gè)觸發(fā)方法
1 call 2 callAsync 3 promise
這一章節(jié) 我們將分別實(shí)現(xiàn)異步的Async版本和Promise版本
異步鉤子
AsyncParallelHook
AsyncParallelHook的Promise版本
AsyncSeriesHook
AsyncSeriesHook的Promise版本
AsyncSeriesWaterfallHook
AsyncSeriesWaterfallHook的Promise版本
異步的鉤子分為并行和串行的鉤子,并行是指 等待所有并發(fā)的異步事件執(zhí)行之后再執(zhí)行最終的異步回調(diào)。
而串行是值 第一步執(zhí)行完畢再去執(zhí)行第二步,以此類推,直到執(zhí)行完所有回調(diào)再去執(zhí)行最終的異步回調(diào)。
AsyncParallelHook
AsyncParallelHook是異步并行的鉤子,上代碼:
const { AsyncParallelHook } = require("tapable"); class Hook{ constructor(){ this.hooks = new AsyncParallelHook(["name"]); } tap(){ /** 異步的注冊(cè)方法是tapAsync() * 并且有回調(diào)函數(shù)cb. */ this.hooks.tapAsync("node",function(name,cb){ setTimeout(()=>{ console.log("node",name); cb(); },1000); }); this.hooks.tapAsync("react",function(name,cb){ setTimeout(()=>{ console.log("react",name); cb(); },1000); }); } start(){ /** 異步的觸發(fā)方法是callAsync() * 多了一個(gè)最終的回調(diào)函數(shù) fn. */ this.hooks.callAsync("call end.",function(){ console.log("最終的回調(diào)"); }); } } let h = new Hook(); h.tap();/** 類似訂閱 */ h.start();/** 類似發(fā)布 */ /* 打印順序: node call end. react call end. 最終的回調(diào) */
等待1s后,分別執(zhí)行了node call end和react callend 最后執(zhí)行了最終的回調(diào)fn.
手動(dòng)實(shí)現(xiàn):
class AsyncParallelHook{ constructor(args){ /* args -> ["name"]) */ this.tasks = []; } /** tap接收兩個(gè)參數(shù) name和fn */ tap(name,fn){ /** 訂閱:將fn放入到this.tasks中 */ this.tasks.push(fn); } start(...args){ let index = 0; /** 通過pop()獲取到最后一個(gè)參數(shù) * finalCallBack() 最終的回調(diào) */ let finalCallBack = args.pop(); /** 箭頭函數(shù)綁定this */ let done = () => { /** 執(zhí)行done() 每次index+1 */ index++; if(index === this.tasks.length){ /** 執(zhí)行最終的回調(diào) */ finalCallBack(); } } this.tasks.forEach((task)=>{ /** 執(zhí)行每個(gè)task,傳入我們給定的done回調(diào)函數(shù) */ task(...args,done); }); } } let h = new AsyncParallelHook(["name"]); /** 訂閱 */ h.tap("react",(name,cb)=>{ setTimeout(()=>{ console.log("react",name); cb(); },1000); }); h.tap("node",(name,cb)=>{ setTimeout(()=>{ console.log("node",name); cb(); },1000); }); /** 發(fā)布 */ h.start("end.",function(){ console.log("最終的回調(diào)函數(shù)"); }); /* 打印順序: react end. node end. 最終的回調(diào)函數(shù) */
AsyncParallelHook的Promise版本
const { AsyncParallelHook } = require("tapable"); class Hook{ constructor(){ this.hooks = new AsyncParallelHook(["name"]); } tap(){ /** 這里是Promsie寫法 * 注冊(cè)事件的方法為tapPromise */ this.hooks.tapPromise("node",function(name){ return new Promise((resolve,reject)=>{ setTimeout(()=>{ console.log("node",name); resolve(); },1000); }); }); this.hooks.tapPromise("react",function(name){ return new Promise((resolve,reject)=>{ setTimeout(()=>{ console.log("react",name); resolve(); },1000); }); }); } start(){ /** * promsie最終返回一個(gè)prosise 成功resolve時(shí) * .then即為最終回調(diào) */ this.hooks.promise("call end.").then(function(){ console.log("最終的回調(diào)"); }); } } let h = new Hook(); h.tap(); h.start(); /* 打印順序: node call end. react call end. 最終的回調(diào) */
這里鉤子還是AsyncParallelHook鉤子,只是寫法變成了promise的寫法,去掉了回調(diào)函數(shù)cb().變成了成功時(shí)去resolve().其實(shí)用Promise可以更好解決異步并行的問題,因?yàn)镻romise的原型方法上有個(gè)all()方法,它的作用就是等待所有promise執(zhí)行完畢后再去執(zhí)行最終的promise。我們現(xiàn)在去實(shí)現(xiàn)它:
class SyncHook{ constructor(args){ this.tasks = []; } tapPromise(name,fn){ this.tasks.push(fn); } promise(...args){ /** 利用map方法返回一個(gè)新數(shù)組的特性 */ let tasks = this.tasks.map((task)=>{ /** 每一個(gè)task都是一個(gè)Promise */ return task(...args); }); /** Promise.all() 等待所有Promise都執(zhí)行完畢 */ return Promise.all(tasks); } } let h = new SyncHook(["name"]); /** 訂閱 */ h.tapPromise("react",(name)=>{ return new Promise((resolve,reject)=>{ setTimeout(()=>{ console.log("react",name); resolve(); },1000); }); }); h.tapPromise("node",(name)=>{ return new Promise((resolve,reject)=>{ setTimeout(()=>{ console.log("node",name); resolve(); },1000); }); }); /** 發(fā)布 */ h.promise("end.").then(function(){ console.log("最終的回調(diào)函數(shù)"); }); /* 打印順序: react end. node end. 最終的回調(diào)函數(shù) */
AsyncSeriesHook
AsyncSeriesHook是異步串行的鉤子, 串行,我們剛才說(shuō)了, 它是一步步去執(zhí)行的,下一步執(zhí)行依賴上一步執(zhí)行是否完成,手動(dòng)實(shí)現(xiàn):
const { AsyncSeriesHook } = require("tapable"); class Hook{ constructor(){ this.hooks = new AsyncSeriesHook(["name"]); } tap(){ /** 異步的注冊(cè)方法是tapAsync() * 并且有回調(diào)函數(shù)cb. */ this.hooks.tapAsync("node",function(name,cb){ setTimeout(()=>{ console.log("node",name); cb(); },1000); }); this.hooks.tapAsync("react",function(name,cb){ /** 此回調(diào)要等待上一個(gè)回調(diào)執(zhí)行完畢后才開始執(zhí)行 */ setTimeout(()=>{ console.log("react",name); cb(); },1000); }); } start(){ /** 異步的觸發(fā)方法是callAsync() * 多了一個(gè)最終的回調(diào)函數(shù) fn. */ this.hooks.callAsync("call end.",function(){ console.log("最終的回調(diào)"); }); } } let h = new Hook(); h.tap(); h.start(); /* 打印順序: node call end. react call end. -> 1s后打印 最終的回調(diào) -> 1s后打印 */
AsyncParallelHook和AsyncSeriesHook的區(qū)別是AsyncSeriesHook是串行的異步鉤子,也就是說(shuō)它會(huì)等待上一步的執(zhí)行 只有上一步執(zhí)行完畢了 才會(huì)開始執(zhí)行下一步。而AsyncParallelHook是并行異步 AsyncParallelHook 是同時(shí)并發(fā)執(zhí)行。 ok.手動(dòng)實(shí)現(xiàn) AsyncSeriesHook:
class AsyncParallelHook{ constructor(args){ /* args -> ["name"]) */ this.tasks = []; } /** tap接收兩個(gè)參數(shù) name和fn */ tap(name,fn){ /** 訂閱:將fn放入到this.tasks中 */ this.tasks.push(fn); } start(...args){ let index = 0; let finalCallBack = args.pop(); /** 遞歸執(zhí)行next()方法 直到執(zhí)行所有task * 最后執(zhí)行最終的回調(diào)finalCallBack() */ let next = () => { /** 直到執(zhí)行完所有task后 * 再執(zhí)行最終的回調(diào) finalCallBack() */ if(index === this.tasks.length){ return finalCallBack(); } /** index++ 執(zhí)行每一個(gè)task 并傳入遞歸函數(shù)next * 執(zhí)行完每個(gè)task后繼續(xù)遞歸執(zhí)行下一個(gè)task * next === cb,next就是每一步的cb回調(diào) */ this.tasks[index++](...args,next); } /** 執(zhí)行next() */ next(); } } let h = new AsyncParallelHook(["name"]); /** 訂閱 */ h.tap("react",(name,cb)=>{ setTimeout(()=>{ console.log("react",name); cb(); },1000); }); h.tap("node",(name,cb)=>{ setTimeout(()=>{ console.log("node",name); cb(); },1000); }); /** 發(fā)布 */ h.start("end.",function(){ console.log("最終的回調(diào)函數(shù)"); }); /* 打印順序: react end. node end. -> 1s后打印 最終的回調(diào)函數(shù) -> 1s后打印 */
AsyncSeriesHook的Promise版本
const { AsyncSeriesHook } = require("tapable"); class Hook{ constructor(){ this.hooks = new AsyncSeriesHook(["name"]); } tap(){ /** 這里是Promsie寫法 * 注冊(cè)事件的方法為tapPromise */ this.hooks.tapPromise("node",function(name){ return new Promise((resolve,reject)=>{ setTimeout(()=>{ console.log("node",name); resolve(); },1000); }); }); this.hooks.tapPromise("react",function(name){ /** 等待上一步 執(zhí)行完畢之后 再執(zhí)行 */ return new Promise((resolve,reject)=>{ setTimeout(()=>{ console.log("react",name); resolve(); },1000); }); }); } start(){ /** * promsie最終返回一個(gè)prosise 成功resolve時(shí) * .then即為最終回調(diào) */ this.hooks.promise("call end.").then(function(){ console.log("最終的回調(diào)"); }); } } let h = new Hook(); h.tap(); h.start(); /* 打印順序: node call end. react call end. -> 1s后打印 最終的回調(diào) -> 1s后打印 */
手動(dòng)實(shí)現(xiàn)AsyncSeriesHook的Promise版本
class AsyncSeriesHook{ constructor(args){ this.tasks = []; } tapPromise(name,fn){ this.tasks.push(fn); } promise(...args){ /** 1 解構(gòu) 拿到第一個(gè)first * first是一個(gè)promise */ let [first, ...others] = this.tasks; /** 4 利用reduce方法 累計(jì)執(zhí)行 * 它最終返回的是一個(gè)Promsie */ return others.reduce((l,n)=>{ /** 1 下一步的執(zhí)行依賴上一步的then */ return l.then(()=>{ /** 2 下一步執(zhí)行依賴上一步結(jié)果 */ return n(...args); }); },first(...args)); } } let h = new AsyncSeriesHook(["name"]); /** 訂閱 */ h.tapPromise("react",(name)=>{ return new Promise((resolve,reject)=>{ setTimeout(()=>{ console.log("react",name); resolve(); },1000); }); }); h.tapPromise("node",(name)=>{ return new Promise((resolve,reject)=>{ setTimeout(()=>{ console.log("node",name); resolve(); },1000); }); }); /** 發(fā)布 */ h.promise("end.").then(function(){ console.log("最終的回調(diào)函數(shù)"); }); /* 打印順序: react end. node end. -> 1s后打印 最終的回調(diào)函數(shù) -> 1s后打印 */
最后一個(gè)AsyncSeriesWaterfallHook:
AsyncSeriesWaterfallHook
AsyncSeriesWaterfallHook 異步的串行的瀑布鉤子,首先 它是一個(gè)異步串行的鉤子,同時(shí) 它的下一步依賴上一步的結(jié)果返回:
const { AsyncSeriesWaterfallHook } = require("tapable"); class Hook{ constructor(){ this.hooks = new AsyncSeriesWaterfallHook(["name"]); } tap(){ this.hooks.tapAsync("node",function(name,cb){ setTimeout(()=>{ console.log("node",name); /** 第一次參數(shù)是err, 第二個(gè)參數(shù)是傳遞給下一步的參數(shù) */ cb(null,"第一步返回第二步的結(jié)果"); },1000); }); this.hooks.tapAsync("react",function(data,cb){ /** 此回調(diào)要等待上一個(gè)回調(diào)執(zhí)行完畢后才開始執(zhí)行 * 并且 data 是上一步return的結(jié)果. */ setTimeout(()=>{ console.log("react",data); cb(); },1000); }); } start(){ this.hooks.callAsync("call end.",function(){ console.log("最終的回調(diào)"); }); } } let h = new Hook(); h.tap(); h.start(); /* 打印順序: node call end. react 第一步返回第二步的結(jié)果 最終的回調(diào) */
我們可以看到 第二步依賴了第一步返回的值, 并且它也是串行的鉤子,實(shí)現(xiàn)它:
class AsyncParallelHook{ constructor(args){ /* args -> ["name"]) */ this.tasks = []; } /** tap接收兩個(gè)參數(shù) name和fn */ tap(name,fn){ /** 訂閱:將fn放入到this.tasks中 */ this.tasks.push(fn); } start(...args){ let index = 0; /** 1 拿到最后的最終的回調(diào) */ let finalCallBack = args.pop(); let next = (err,data) => { /** 拿到每個(gè)task */ let task = this.tasks[index]; /** 2 如果沒傳task 或者全部task都執(zhí)行完畢 * return 直接執(zhí)行最終的回調(diào)finalCallBack() */ if(!task) return finalCallBack(); if(index === 0){ /** 3 執(zhí)行第一個(gè)task * 并傳遞參數(shù)為原始參數(shù)args */ task(...args, next); }else{ /** 4 執(zhí)行處第二個(gè)外的每個(gè)task * 并傳遞的參數(shù) data * data ->‘傳遞給下一步的結(jié)果’ */ task(data, next); } index++; } /** 執(zhí)行next() */ next(); } } let h = new AsyncParallelHook(["name"]); /** 訂閱 */ h.tap("react",(name,cb)=>{ setTimeout(()=>{ console.log("react",name); cb(null,"傳遞給下一步的結(jié)果"); },1000); }); h.tap("node",(name,cb)=>{ setTimeout(()=>{ console.log("node",name); cb(); },1000); }); /** 發(fā)布 */ h.start("end.",function(){ console.log("最終的回調(diào)函數(shù)"); }); /* 打印順序: react end. node 傳遞給下一步的結(jié)果 最終的回調(diào)函數(shù) */
AsyncSeriesWaterfallHook的Promise版本
const { AsyncSeriesWaterfallHook } = require("tapable"); class Hook{ constructor(){ this.hooks = new AsyncSeriesWaterfallHook(["name"]); } tap(){ this.hooks.tapPromise("node",function(name){ return new Promise((resolve,reject)=>{ setTimeout(()=>{ console.log("node",name); /** 在resolve中把結(jié)果傳給下一步 */ resolve("返回給下一步的結(jié)果"); },1000); }); }); this.hooks.tapPromise("react",function(name){ return new Promise((resolve,reject)=>{ setTimeout(()=>{ console.log("react",name); resolve(); },1000); }); }); } start(){ this.hooks.promise("call end.").then(function(){ console.log("最終的回調(diào)"); }); } } let h = new Hook(); h.tap(); h.start(); /* 打印順序: node call end. react 返回給下一步的結(jié)果 最終的回調(diào) */
用Promsie實(shí)現(xiàn)很簡(jiǎn)單,手動(dòng)實(shí)現(xiàn)它吧:
class AsyncSeriesHook{ constructor(args){ this.tasks = []; } tapPromise(name,fn){ this.tasks.push(fn); } promise(...args){ /** 1 解構(gòu) 拿到第一個(gè)first * first是一個(gè)promise */ let [first, ...others] = this.tasks; /** 2 利用reduce方法 累計(jì)執(zhí)行 * 它最終返回的是一個(gè)Promsie */ return others.reduce((l,n)=>{ return l.then((data)=>{ /** 3 將data傳給下一個(gè)task 即可 */ return n(data); }); },first(...args)); } } let h = new AsyncSeriesHook(["name"]); /** 訂閱 */ h.tapPromise("react",(name)=>{ return new Promise((resolve,reject)=>{ setTimeout(()=>{ console.log("react",name); resolve("promise-傳遞給下一步的結(jié)果"); },1000); }); }); h.tapPromise("node",(name)=>{ return new Promise((resolve,reject)=>{ setTimeout(()=>{ console.log("node",name); resolve(); },1000); }); }); /** 發(fā)布 */ h.promise("end.").then(function(){ console.log("最終的回調(diào)函數(shù)"); }); /* 打印順序: react end. node promise-傳遞給下一步的結(jié)果 最終的回調(diào)函數(shù) */
ok.至此,我們把tapable的鉤子全部解析并手動(dòng)實(shí)現(xiàn)完畢。寫文章不易,喜歡的話給個(gè)贊或者start~
代碼在github上:mock-webpack-tapable
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/102161.html
摘要:本文將根據(jù)以下章節(jié)分別梳理每個(gè)鉤子同步鉤子首先安裝是簡(jiǎn)單的同步鉤子,它很類似于發(fā)布訂閱。至此,我們把的所有同步鉤子都解析完畢異步鉤子比同步鉤子麻煩些,我們會(huì)在下一章節(jié)開始解析異步的鉤子傳送門深入理解核心模塊鉤子異步版代碼 記錄下自己在前端路上爬坑的經(jīng)歷 加深印象,正文開始~ tapable是webpack的核心依賴庫(kù) 想要讀懂webpack源碼 就必須首先熟悉tapableok.下面是...
摘要:引入定義一個(gè)自己的插件。一個(gè)最基礎(chǔ)的的代碼是這樣的在構(gòu)造函數(shù)中獲取用戶給該插件傳入的配置會(huì)調(diào)用實(shí)例的方法給插件實(shí)例傳入對(duì)象導(dǎo)出在使用這個(gè)時(shí),相關(guān)配置代碼如下和在開發(fā)時(shí)最常用的兩個(gè)對(duì)象就是和,它們是和之間的橋梁。 本文示例源代碼請(qǐng)戳github博客,建議大家動(dòng)手敲敲代碼。 webpack本質(zhì)上是一種事件流的機(jī)制,它的工作流程就是將各個(gè)插件串聯(lián)起來(lái),而實(shí)現(xiàn)這一切的核心就是Tapable,w...
摘要:系列文章系列第一篇基礎(chǔ)雜記系列第二篇插件機(jī)制雜記系列第三篇流程雜記前言本身并不難,他所完成的各種復(fù)雜炫酷的功能都依賴于他的插件機(jī)制。的插件機(jī)制依賴于一個(gè)核心的庫(kù),。是什么是一個(gè)類似于的的庫(kù)主要是控制鉤子函數(shù)的發(fā)布與訂閱。 系列文章 Webpack系列-第一篇基礎(chǔ)雜記 Webpack系列-第二篇插件機(jī)制雜記 Webpack系列-第三篇流程雜記 前言 webpack本身并不難,他所完成...
摘要:但也是最復(fù)雜的一個(gè)。中當(dāng)一旦某個(gè)返回值結(jié)果不為便結(jié)束執(zhí)行列表中的插件中上一個(gè)插件執(zhí)行結(jié)果當(dāng)作下一個(gè)插件的入?yún)⒄{(diào)用并行執(zhí)行插件流程篇本文關(guān)于的流程講解是基于的。 webpack是現(xiàn)代前端開發(fā)中最火的模塊打包工具,只需要通過簡(jiǎn)單的配置,便可以完成模塊的加載和打包。那它是怎么做到通過對(duì)一些插件的配置,便可以輕松實(shí)現(xiàn)對(duì)代碼的構(gòu)建呢? webpack的配置 const path = requir...
摘要:用于對(duì)模塊的源代碼進(jìn)行轉(zhuǎn)換。將基礎(chǔ)模塊打包進(jìn)動(dòng)態(tài)鏈接庫(kù),當(dāng)依賴的模塊存在于動(dòng)態(tài)鏈接庫(kù)中時(shí),無(wú)需再次打包,而是直接從動(dòng)態(tài)鏈接庫(kù)中獲取。負(fù)責(zé)打包出動(dòng)態(tài)鏈接庫(kù),負(fù)責(zé)從主要配置文件中引入插件打包好的動(dòng)態(tài)鏈接庫(kù)文件。告一段落,淺嘗輒止。 吐槽一下 webpack 自出現(xiàn)時(shí),一直備受青睞。作為強(qiáng)大的打包工具,它只是出現(xiàn)在項(xiàng)目初始或優(yōu)化的階段。如果沒有參與項(xiàng)目的構(gòu)建,接觸的機(jī)會(huì)幾乎為零。即使是參與了...
閱讀 1626·2021-10-25 09:46
閱讀 3209·2021-10-08 10:04
閱讀 2354·2021-09-06 15:00
閱讀 2768·2021-08-19 10:57
閱讀 2077·2019-08-30 11:03
閱讀 970·2019-08-30 11:00
閱讀 2370·2019-08-26 17:10
閱讀 3545·2019-08-26 13:36