Tapable
最近想做一个前端小工具,考虑全部做成插件式的 ,由核心代码去调用整合所有的插件,之前有研究过 Webpack
的源码,觉得 Webpack
的插件系统写的还是很牛的,这得益于它官方的核心事件库 Tapable
。
之前研究 Webpack
源码的时候并没有深入研究 Tabable
,正好可以补上,下面跟着我的节奏一起撸一下 Tabable
源码!
简介
Tapable
库是一个事件流机制的核心工具,它提供了多种 Hook 类型,用于管理插件和自定义事件流。
一、同步 Hooks
SyncHook
同步串行执行所有注册的回调函数,不关心返回值。
示例
const SyncHook = require("../lib/SyncHook");
const syncHook = new SyncHook(["name", "age"], "user");
syncHook.tap("syncHook_tap1", function (name, age) {
console.log("syncHook_tap1", name, age);
});
// syncHook.tap({ name: "syncHook_tap2",before: "syncHook_tap1" }, function (name, age) {
// console.log("syncHook_tap2", name, age);
// });
syncHook.tap({ name: "syncHook_tap2",stage: -1 }, function (name, age) {
console.log("syncHook_tap2", name, age);
});
syncHook.call("yasin", 18);
分析
- new SyncHook([“name”, “age”], “user”) 执行后,最后 call 方法的生成如下:
function call(name, age) { // 传进去的 ["name", "age"] 参数被当成了动态方法的参数名称
"use strict";
var _context;
var _x = this._x;
var _fn0 = _x[0];
_fn0(name, age);
var _fn1 = _x[1];
_fn1(name, age);
}
- 默认顺序是先执行 tab 方法先执行:tab1->tab2
- 可用加 before 参数改变默认顺序,例如 { name: “syncHook_tap2”,before: “syncHook_tap1” }:tab2->tab1
- 可用加 stage 参数改变默认顺序,表示权重,默认 0(越小越前执行),例如 { name: “syncHook_tap2”,stage: -1 }:tab2->tab1
作用
-
事件通知(如日志记录)
const { SyncHook } = require('tapable'); const hook = new SyncHook(['arg']); hook.tap('Logger1', (arg) => console.log('Logger1:', arg)); hook.tap('Logger2', (arg) => console.log('Logger2:', arg)); hook.call('Hello'); // 依次输出 Logger1: Hello 和 Logger2: Hello
SyncBailHook
同步串行执行回调,若某个回调返回 非 undefined
,则停止后续回调。
示例
const SyncBailHook = require("../lib/SyncBailHook");
const synBailHook = new SyncBailHook(["name", "age"], "user");
synBailHook.tap("synBailHook_tap1", function (name, age) {
console.log("synBailHook_tap1", name, age);
});
// syncHook.tap({ name: "syncHook_tap2",before: "syncHook_tap1" }, function (name, age) {
// console.log("syncHook_tap2", name, age);
// });
synBailHook.tap({ name: "synBailHook_tap2",stage: -1 }, function (name, age) {
console.log("synBailHook_tap2", name, age);
});
synBailHook.call("yasin", 18);
分析
-
new SyncBailHook([“name”, “age”], “user”) 执行后,最后 call 方法的生成如下:
function call(name, age) { "use strict"; var _context; var _x = this._x; var _fn0 = _x[0]; var _result0 = _fn0(name, age); if(_result0 !== undefined) { return _result0; } else { var _fn1 = _x[1]; var _result1 = _fn1(name, age); if(_result1 !== undefined) { return _result1; ; } else { } } }
有人说,那么 tab 方法调用次数多了会变成什么样呢?
... synBailHook.tap({ name: "synBailHook_tap3"}, function (name, age) { console.log("synBailHook_tap3", name, age); }); synBailHook.tap({ name: "synBailHook_tap4"}, function (name, age) { console.log("synBailHook_tap4", name, age); }); synBailHook.call("yasin", 18);
最后生成的 call 方法为:
function call(name, age) { "use strict"; var _context; var _x = this._x; var _fn0 = _x[0]; var _result0 = _fn0(name, age); if(_result0 !== undefined) { return _result0; ; } else { var _fn1 = _x[1]; var _result1 = _fn1(name, age); if(_result1 !== undefined) { return _result1; ; } else { var _fn2 = _x[2]; var _result2 = _fn2(name, age); if(_result2 !== undefined) { return _result2; ; } else { var _fn3 = _x[3]; var _result3 = _fn3(name, age); if(_result3 !== undefined) { return _result3; ; } else { } } } } }
看吧,就是这么简单粗暴,但是很高效!
其它的参数什么的就跟 SyncHook 差不多了。
作用
-
安全检查(如权限验证)
const { SyncBailHook } = require('tapable'); const hook = new SyncBailHook(['user']); hook.tap('CheckAdmin', (user) => { if (user.role !== 'admin') return false; // 终止后续执行 }); hook.tap('LogAccess', (user) => console.log(`${user.name} accessed.`)); const result = hook.call({ name: 'Alice', role: 'user' }); // 仅执行 CheckAdmin,返回 false,LogAccess 不会执行
SyncWaterfallHook
同步串行执行回调,前一个回调的返回值会作为参数传给下一个回调。
示例
const SyncWaterfallHook = require("../lib/SyncWaterfallHook");
const syncWaterfallHook = new SyncWaterfallHook(["name", "age"], "user");
syncWaterfallHook.tap("syncWaterfallHook_tap1", function (name, age) {
console.log("syncWaterfallHook_tap1", name, age);
});
// syncHook.tap({ name: "syncHook_tap2",before: "syncHook_tap1" }, function (name, age) {
// console.log("syncHook_tap2", name, age);
// });
syncWaterfallHook.tap({ name: "syncWaterfallHook_tap2",stage: -1 }, function (name, age) {
console.log("syncWaterfallHook_tap2", name, age);
});
syncWaterfallHook.tap({ name: "syncWaterfallHook_tap3"}, function (name, age) {
console.log("syncWaterfallHook_tap3", name, age);
});
syncWaterfallHook.tap({ name: "syncWaterfallHook_tap4"}, function (name, age) {
console.log("syncWaterfallHook_tap4", name, age);
});
syncWaterfallHook.call("yasin", 18);
分析
-
new SyncWaterfallHook([“name”, “age”], “user”) 执行后,最后 call 方法的生成如下:
function call(name, age) { "use strict"; var _context; var _x = this._x; var _fn0 = _x[0]; var _result0 = _fn0(name, age); if(_result0 !== undefined) { name = _result0; } var _fn1 = _x[1]; var _result1 = _fn1(name, age); if(_result1 !== undefined) { name = _result1; } var _fn2 = _x[2]; var _result2 = _fn2(name, age); if(_result2 !== undefined) { name = _result2; } var _fn3 = _x[3]; var _result3 = _fn3(name, age); if(_result3 !== undefined) { name = _result3; } return name; }
从源码我们可以看出,我们虽然传入了 age、name 两个参数,但是但凡有一个钩子函数返回了非 undefine 的值的时候,就会将前一个回调的返回值会作为参数传给下一个回调。
比如我们修改一下 syncWaterfallHook_tap3 钩子函数:
const SyncWaterfallHook = require("../lib/SyncWaterfallHook"); const syncWaterfallHook = new SyncWaterfallHook(["name", "age"], "user"); syncWaterfallHook.tap("syncWaterfallHook_tap1", function (name, age) { console.log("syncWaterfallHook_tap1", name, age); }); syncWaterfallHook.tap({ name: "syncWaterfallHook_tap2",stage: -1 }, function (name, age) { console.log("syncWaterfallHook_tap2", name, age); }); syncWaterfallHook.tap({ name: "syncWaterfallHook_tap3"}, function (name, age) { console.log("syncWaterfallHook_tap3", name, age); return "我被 tab3 函数改改成了小白"; }); syncWaterfallHook.tap({ name: "syncWaterfallHook_tap4"}, function (name, age) { console.log("syncWaterfallHook_tap4", name, age); }); syncWaterfallHook.call("yasin", 18);
执行结果:
syncWaterfallHook_tap2 yasin 18 syncWaterfallHook_tap1 yasin 18 syncWaterfallHook_tap3 yasin 18 syncWaterfallHook_tap4 我被 tab3 函数改改成了小白 18
可以看到,name 参数被 tab3 钩子函数的 return 值改掉了,不过从源码也可以看出,SyncWaterfallHook 也只能修改第一个 name 参数的值,并不会修改 age 的值。
作用
-
数据处理管道(如字符串处理链)
const { SyncWaterfallHook } = require('tapable'); const hook = new SyncWaterfallHook(['text']); hook.tap('Trim', (text) => text.trim()); hook.tap('UpperCase', (text) => text.toUpperCase()); const result = hook.call(' hello '); // 输出 "HELLO"
SyncLoopHook
同步循环执行回调,若某个回调返回 true
,则重复执行,直到返回 undefined
。
示例
const SyncLoopHook = require("../lib/SyncLoopHook");
const syncLoopHook = new SyncLoopHook(["name", "age"], "user");
syncLoopHook.tap("syncLoopHook_tap1", function (name, age) {
console.log("syncLoopHook_tap1", name, age);
});
syncLoopHook.tap({ name: "syncLoopHook_tap2",stage: -1 }, function (name, age) {
console.log("syncLoopHook_tap2", name, age);
});
syncLoopHook.tap({ name: "syncLoopHook_tap3"}, function (name, age) {
console.log("syncLoopHook_tap3", name, age);
// return "我被 tab3 函数改改成了小白";
});
syncLoopHook.tap({ name: "syncLoopHook_tap4"}, function (name, age) {
console.log("syncLoopHook_tap4", name, age);
});
syncLoopHook.call("yasin", 18);
分析
-
看一下 SyncLoopHook 构造的 call 函数:
(function anonymous(name, age ) { "use strict"; var _context; var _x = this._x; var _loop; do { _loop = false; var _fn0 = _x[0]; var _result0 = _fn0(name, age); if(_result0 !== undefined) { _loop = true; } else { var _fn1 = _x[1]; var _result1 = _fn1(name, age); if(_result1 !== undefined) { _loop = true; } else { var _fn2 = _x[2]; var _result2 = _fn2(name, age); if(_result2 !== undefined) { _loop = true; } else { var _fn3 = _x[3]; var _result3 = _fn3(name, age); if(_result3 !== undefined) { _loop = true; } else { if(!_loop) { } } } } } } while (_loop); })
可以看到,就是一个
do{}while()
语句,遇到有return true
的钩子函数的时候就一直循环执行所有钩子函数。
作用
-
轮询直到条件满足(如重试机制)
const { SyncLoopHook } = require('tapable'); let retries = 0; const hook = new SyncLoopHook(); hook.tap('Retry', () => { retries++; return retries < 3 ? true : undefined; // 重试 3 次后停止 }); hook.call(); // 回调执行 3 次
二、异步 Hooks
AsyncSeriesHook
异步串行执行回调,按注册顺序执行,需手动调用 callback
或返回 Promise
。
示例
const AsyncSeriesHook = require("../lib/AsyncSeriesHook");
const asyncSeriesHook = new AsyncSeriesHook(["name", "age"], "user");
asyncSeriesHook.tapAsync("asyncSeriesHook_tap1", function (name, age, callback) {
console.log("asyncSeriesHook_tap1", name, age);
setTimeout(callback,1000);
});
asyncSeriesHook.tapAsync({
name: "asyncSeriesHook_tap2",
stage: -1
}, function (name, age, callback) {
console.log("asyncSeriesHook_tap2", name, age);
setTimeout(callback,1000);
});
asyncSeriesHook.tapAsync({name: "asyncSeriesHook_tap3"}, function (name, age, callback) {
console.log("asyncSeriesHook_tap3", name, age);
setTimeout(callback,1000);
});
asyncSeriesHook.tapAsync({name: "asyncSeriesHook_tap4"}, function (name, age, callback) {
console.log("asyncSeriesHook_tap4", name, age);
setTimeout(()=>callback("我是 tap4,任务完成!"),1000);
});
asyncSeriesHook.callAsync("yasin", 18, (result) => {
console.log("result->", result);
});
执行结果
asyncSeriesHook_tap2 yasin 18
asyncSeriesHook_tap1 yasin 18
asyncSeriesHook_tap3 yasin 18
asyncSeriesHook_tap4 yasin 18
result-> 我是 tap4,任务完成!
(因为 tab2 的 stage 被我们修改成了 -1,所以它最先执行)每个 tab 钩子函数间隔 1s 顺序执行函数,直到 tab4 最后一个钩子返回了 result 的值。
分析
-
看一下 SyncLoopHook 构造的 callAsync 函数:
(function anonymous(name, age, _callback ) { "use strict"; var _context; var _x = this._x; function _next2() { var _fn3 = _x[3]; _fn3(name, age, (function (_err3) { if(_err3) { _callback(_err3); } else { _callback(); } })); } function _next1() { var _fn2 = _x[2]; _fn2(name, age, (function (_err2) { if(_err2) { _callback(_err2); } else { _next2(); } })); } function _next0() { var _fn1 = _x[1]; _fn1(name, age, (function (_err1) { if(_err1) { _callback(_err1); } else { _next1(); } })); } var _fn0 = _x[0]; _fn0(name, age, (function (_err0) { if(_err0) { _callback(_err0); } else { _next0(); } })); })
从源码我们可以看出:
- 只有上一个 tab 钩子函数调用的 callback 函数,下一个 tab 钩子函数才会执行。
- 只要有一个 tab 钩子函数调用了 callback 函数,并且传递了值,整个链路就直接结束了。
-
将 tab3、tab4 钩子函数改成 tabPromise 方法:
const AsyncSeriesHook = require("../lib/AsyncSeriesHook"); const asyncSeriesHook = new AsyncSeriesHook(["name", "age"], "user"); asyncSeriesHook.tapAsync("asyncSeriesHook_tap1", function (name, age, callback) { console.log("asyncSeriesHook_tap1", name, age); setTimeout(callback, 1000); }); asyncSeriesHook.tapAsync({ name: "asyncSeriesHook_tap2", stage: -1 }, function (name, age, callback) { console.log("asyncSeriesHook_tap2", name, age); setTimeout(callback, 1000); }); asyncSeriesHook.tapPromise({name: "asyncSeriesHook_tap3"}, function (name, age) { console.log("asyncSeriesHook_tap3", name, age); return new Promise((resolve)=>{ setTimeout(resolve, 1000); }) }); asyncSeriesHook.tapPromise({name: "asyncSeriesHook_tap4"}, function (name, age) { console.log("asyncSeriesHook_tap4", name, age); return new Promise((resolve, reject)=>{ setTimeout(()=>reject("我是 tap4,任务完成!"), 1000); }) }); asyncSeriesHook.callAsync("yasin", 18, (result) => { console.log("result->", result); });
执行结果跟前面 tabAsync 函数一致:
asyncSeriesHook_tap2 yasin 18 asyncSeriesHook_tap1 yasin 18 asyncSeriesHook_tap3 yasin 18 asyncSeriesHook_tap4 yasin 18 result-> 我是 tap4,任务完成!
来看一下生成的 callAsync 函数:
(function anonymous(name, age, _callback ) { "use strict"; var _context; var _x = this._x; function _next2() { var _fn3 = _x[3]; var _hasResult3 = false; var _promise3 = _fn3(name, age); if(!_promise3 || !_promise3.then) throw new Error('Tap function (tapPromise) did not return promise (returned ' + _promise3 + ')'); _promise3.then((function (_result3) { _hasResult3 = true; _callback(); }), function (_err3) { if(_hasResult3) throw _err3; _callback(_err3); }); } function _next1() { var _fn2 = _x[2]; var _hasResult2 = false; var _promise2 = _fn2(name, age); if(!_promise2 || !_promise2.then) throw new Error('Tap function (tapPromise) did not return promise (returned ' + _promise2 + ')'); _promise2.then((function (_result2) { _hasResult2 = true; _next2(); }), function (_err2) { if(_hasResult2) throw _err2; _callback(_err2); }); } function _next0() { var _fn1 = _x[1]; _fn1(name, age, (function (_err1) { if(_err1) { _callback(_err1); } else { _next1(); } })); } var _fn0 = _x[0]; _fn0(name, age, (function (_err0) { if(_err0) { _callback(_err0); } else { _next0(); } })); })
可以看到,只是把 callback 函数缓存了 Promise.then,其它的都差不多,当某个 tab 钩子函数调用了 Promise.reject 后,整个过程就会立即终止,并返回 reject 的结果至 callAsync 函数。
作用
-
异步任务队列(如按顺序读取文件)。
const { AsyncSeriesHook } = require('tapable'); const hook = new AsyncSeriesHook(['file']); hook.tapAsync('ReadFile', (file, callback) => { setTimeout(() => { console.log(`Read ${file}`); callback(); }, 500); }); hook.tapAsync('ProcessFile', (file, callback) => { setTimeout(() => { console.log(`Process ${file}`); callback(); }, 300); }); hook.callAsync('data.txt', () => console.log('All done!')); // 输出顺序:Read data.txt → Process data.txt → All done!
AsyncParallelHook
异步并行执行所有回调,所有回调完成后触发最终回调。
示例
const AsyncParallelHook = require("../lib/AsyncParallelHook");
const asyncParallelHook = new AsyncParallelHook(["name", "age"], "user");
asyncParallelHook.tapAsync("asyncParallelHook_tap1", function (name, age, callback) {
console.log("asyncParallelHook_tap1", name, age);
setTimeout(callback, 1000);
});
asyncParallelHook.tapAsync({
name: "asyncParallelHook_tap2",
stage: -1
}, function (name, age, callback) {
console.log("asyncParallelHook_tap2", name, age);
setTimeout(callback, 1000);
});
asyncParallelHook.tapPromise({name: "asyncParallelHook_tap3"}, function (name, age) {
console.log("asyncParallelHook_tap3", name, age);
return new Promise((resolve)=>{
setTimeout(resolve, 1000);
})
});
asyncParallelHook.tapPromise({name: "asyncParallelHook_tap4"}, function (name, age) {
console.log("asyncParallelHook_tap4", name, age);
return new Promise((resolve, reject)=>{
setTimeout(()=>reject("我是 tap4,任务完成!"), 1000);
})
});
asyncParallelHook.callAsync("yasin", 18, (result) => {
console.log("result->", result);
});
分析
-
看一下 AsyncParallelHook 构造的 callAsync 函数:
(function anonymous(name, age, _callback ) { "use strict"; var _context; var _x = this._x; do { var _counter = 4; // 定义总共有多少个 tab 钩子函数 var _done = (function () { _callback(); }); if(_counter <= 0) break; var _fn0 = _x[0]; _fn0(name, age, (function (_err0) { if(_err0) { // 如果有一个 tab 钩子函数调用的 callback 并传递了值 if(_counter > 0) { _callback(_err0); // 直接结束整个过程 _counter = 0; } } else { // 调用 callback 没有传值,当前 tab 钩子函数结束,如果所有钩子函数都执行完成,就直接结束整个流程 if(--_counter === 0) _done(); } })); if(_counter <= 0) break; var _fn1 = _x[1]; _fn1(name, age, (function (_err1) { if(_err1) { if(_counter > 0) { _callback(_err1); _counter = 0; } } else { if(--_counter === 0) _done(); } })); if(_counter <= 0) break; var _fn2 = _x[2]; var _hasResult2 = false; var _promise2 = _fn2(name, age); if(!_promise2 || !_promise2.then) throw new Error('Tap function (tapPromise) did not return promise (returned ' + _promise2 + ')'); _promise2.then((function (_result2) { _hasResult2 = true; if(--_counter === 0) _done(); }), function (_err2) { if(_hasResult2) throw _err2; if(_counter > 0) { _callback(_err2); _counter = 0; } }); if(_counter <= 0) break; var _fn3 = _x[3]; var _hasResult3 = false; var _promise3 = _fn3(name, age); if(!_promise3 || !_promise3.then) throw new Error('Tap function (tapPromise) did not return promise (returned ' + _promise3 + ')'); _promise3.then((function (_result3) { _hasResult3 = true; if(--_counter === 0) _done(); }), function (_err3) { if(_hasResult3) throw _err3; if(_counter > 0) { _callback(_err3); _counter = 0; } }); } while (false); })
- 定义总共有多少个 tab 钩子函数
_counter = 4
- 如果有一个 tab 钩子函数调用的 callback 并传递了值
- 调用 callback 没有传值,当前 tab 钩子函数结束
- 如果所有钩子函数都执行完成,就直接结束整个流程
- 定义总共有多少个 tab 钩子函数
作用
-
并行请求(如同时发起多个 API 请求)。
const { AsyncParallelHook } = require('tapable'); const hook = new AsyncParallelHook(['url']); hook.tapAsync('Request1', (url, callback) => { setTimeout(() => { console.log('Request1 to', url); callback(); }, 1000); }); hook.tapAsync('Request2', (url, callback) => { setTimeout(() => { console.log('Request2 to', url); callback(); }, 500); }); hook.callAsync('/api', () => console.log('All requests done!')); // 输出顺序:Request2 → Request1 → All requests done!(按完成时间)
AsyncSeriesWaterfallHook
异步并行执行所有回调,所有回调完成后触发最终回调。
示例
const AsyncSeriesWaterfallHook = require("../lib/AsyncSeriesWaterfallHook");
const asyncSeriesWaterfallHook = new AsyncSeriesWaterfallHook(["name", "age"], "user");
asyncSeriesWaterfallHook.tapAsync("asyncSeriesWaterfallHook_tap1", function (name, age, callback) {
console.log("asyncSeriesWaterfallHook_tap1", name, age);
setTimeout(callback, 1000);
});
asyncSeriesWaterfallHook.tapAsync({
name: "asyncSeriesWaterfallHook_tap2",
stage: -1
}, function (name, age, callback) {
console.log("asyncSeriesWaterfallHook_tap2", name, age);
setTimeout(callback, 1000);
});
asyncSeriesWaterfallHook.tapPromise({name: "asyncSeriesWaterfallHook_tap3"}, function (name, age) {
console.log("asyncSeriesWaterfallHook_tap3", name, age);
return new Promise((resolve)=>{
setTimeout(()=>resolve("我叫小白,我的名称被 tab3 修改了"), 1000);
})
});
asyncSeriesWaterfallHook.tapPromise({name: "asyncSeriesWaterfallHook_tap4"}, function (name, age) {
console.log("asyncSeriesWaterfallHook_tap4", name, age);
return new Promise((resolve, reject)=>{
setTimeout(()=>reject("我是 tap4,任务完成!"), 1000);
})
});
asyncSeriesWaterfallHook.callAsync("yasin", 18, (result) => {
console.log("result->", result);
});
执行结果:
asyncSeriesWaterfallHook_tap2 yasin 18
asyncSeriesWaterfallHook_tap1 yasin 18
asyncSeriesWaterfallHook_tap3 yasin 18
asyncSeriesWaterfallHook_tap4 我叫小白,我的名称被 tab3 修改了 18
result-> 我是 tap4,任务完成!
分析
-
看一下 AsyncSeriesWaterfallHook 构造的 callAsync 函数:
(function anonymous(name, age, _callback ) { "use strict"; var _context; var _x = this._x; function _next2() { var _fn3 = _x[3]; var _hasResult3 = false; var _promise3 = _fn3(name, age); if(!_promise3 || !_promise3.then) throw new Error('Tap function (tapPromise) did not return promise (returned ' + _promise3 + ')'); _promise3.then((function (_result3) { _hasResult3 = true; if(_result3 !== undefined) { name = _result3; } _callback(null, name); }), function (_err3) { if(_hasResult3) throw _err3; _callback(_err3); }); } function _next1() { var _fn2 = _x[2]; var _hasResult2 = false; var _promise2 = _fn2(name, age); if(!_promise2 || !_promise2.then) throw new Error('Tap function (tapPromise) did not return promise (returned ' + _promise2 + ')'); _promise2.then((function (_result2) { _hasResult2 = true; if(_result2 !== undefined) { name = _result2; } _next2(); }), function (_err2) { if(_hasResult2) throw _err2; _callback(_err2); }); } function _next0() { var _fn1 = _x[1]; _fn1(name, age, (function (_err1, _result1) { if(_err1) { _callback(_err1); } else { if(_result1 !== undefined) { name = _result1; } _next1(); } })); } var _fn0 = _x[0]; _fn0(name, age, (function (_err0, _result0) { if(_err0) { _callback(_err0); } else { if(_result0 !== undefined) { name = _result0; } _next0(); } })); })
- tab 钩子函数按顺序异步执行
- 有一个 tab 钩子函数调用了 callback(“错误信息”, “本次 tab 的结果”),第一个参数如果有值的话,整个过程直接结束,并返回错误信息结果。
- 有一个 tab 钩子函数调用了 Promise.reject,整个过程直接结束,并返回错误信息结果。
- 有一个 tab 钩子函数调用了 callback(undefined, “本次 tab 的结果”),第一个参数没有值,第二个参数有值,就会把第二个参数的值带给下一个 tab 钩子函数直至整个过程结束。
- 有一个 tab 钩子函数调用了 Promise.resolve(xxx),就会 xxx 的值带给下一个 tab 钩子函数直至整个过程结束。
作用
-
并行请求(如同时发起多个 API 请求)
const { AsyncParallelHook } = require('tapable'); const hook = new AsyncParallelHook(['url']); hook.tapAsync('Request1', (url, callback) => { setTimeout(() => { console.log('Request1 to', url); callback(); }, 1000); }); hook.tapAsync('Request2', (url, callback) => { setTimeout(() => { console.log('Request2 to', url); callback(); }, 500); }); hook.callAsync('/api', () => console.log('All requests done!')); // 输出顺序:Request2 → Request1 → All requests done!(按完成时间)
三、总结
Hook 类型 | 执行方式 | 返回值处理 | 典型场景 |
---|---|---|---|
SyncHook | 同步串行 | 忽略返回值 | 事件通知、日志记录 |
SyncBailHook | 同步串行 | 非 undefined 终止后续 | 条件检查、权限拦截 |
SyncWaterfallHook | 同步串行 | 传递返回值 | 数据加工链 |
SyncLoopHook | 同步循环 | true 触发循环 | 轮询、重试 |
AsyncSeriesHook | 异步串行 | 按顺序完成所有任务 | 依赖异步任务(如文件处理) |
AsyncParallelHook | 异步并行 | 所有任务并行完成 | 独立异步任务(如并发请求) |
AsyncSeriesWaterfallHook | 异步串行 | 传递异步返回值 | 异步数据依赖处理 |
通过组合这些 Hook,可以灵活控制 Webpack 插件或自定义工具的事件流逻辑。
VX扫码关注我,获取更多作者动态: