Tapable:Webpack 的事件流核心引擎

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);
分析
  1. 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);
}
  1. 默认顺序是先执行 tab 方法先执行:tab1->tab2
  2. 可用加 before 参数改变默认顺序,例如 { name: “syncHook_tap2”,before: “syncHook_tap1” }:tab2->tab1
  3. 可用加 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);
分析
  1. 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);

分析
  1. 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);

分析
  1. 看一下 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 的值。

分析
  1. 看一下 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 函数,并且传递了值,整个链路就直接结束了。
  2. 将 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);
});
分析
  1. 看一下 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);
    })
    
    1. 定义总共有多少个 tab 钩子函数 _counter = 4
    2. 如果有一个 tab 钩子函数调用的 callback 并传递了值
    3. 调用 callback 没有传值,当前 tab 钩子函数结束
    4. 如果所有钩子函数都执行完成,就直接结束整个流程
作用
  • 并行请求(如同时发起多个 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,任务完成!
分析
  1. 看一下 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();
        }
      }));
    
    })
    
    1. tab 钩子函数按顺序异步执行
    2. 有一个 tab 钩子函数调用了 callback(“错误信息”, “本次 tab 的结果”),第一个参数如果有值的话,整个过程直接结束,并返回错误信息结果。
    3. 有一个 tab 钩子函数调用了 Promise.reject,整个过程直接结束,并返回错误信息结果。
    4. 有一个 tab 钩子函数调用了 callback(undefined, “本次 tab 的结果”),第一个参数没有值,第二个参数有值,就会把第二个参数的值带给下一个 tab 钩子函数直至整个过程结束。
    5. 有一个 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扫码关注我,获取更多作者动态:
请添加图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值