We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Tabpable 类似node eventemitter的库 控制钩子的函数的发布与订阅,控制webpack的插件系统
SyncHook 同步钩子
SyncBailHook 同步熔断钩子
SyncWaterfallHook 同步流水钩子
SyncLoopHook 同步循环钩子
AsyncParalleHook 异步并发钩子
AsyncParalleBailHook 异步并发熔断钩子
AsyncSeriesHook 异步串行钩子
AsyncSeriesBailHook 异步串行熔断钩子
AysncSeriesWaterfallHook 异步串行流水钩子
从功能区分
从类型区分
const hook = new SyncHook(["arg1", "arg2", "arg3"]); class Car { constructor() { this.hooks = { accelerate: new SyncHook(["newSpeed"]), brake: new SyncHook(), calculateRoutes: new AsyncParallelHook(["source", "target", "routesList"]) }; } } const myCar = new Car(); // 使用tab注册同步钩子的监听函数 myCar.hooks.brake.tap("WarningLampPlugin", () => warningLamp.on()); // tab注册的同步钩子使用call方法进行调用 this.hooks.brake.call(); // 使用tab注册同步钩子的监听函数,与上面不同的是,这里的监听函数接受了一个参数 myCar.hooks.accelerate.tap("LoggerPlugin", newSpeed => console.log(`Accelerating to ${newSpeed}`)); // tab注册的同步钩子使用call方法进行调用,这里传入了一个参数,需要注意的是new SyncHook(["newSpeed"])实例化的时候定义了几个参数,那么在调用钩子时传入的参数就可以接收几个,如果超过则多的参数不会被传递,少的参数会被设置为undefined this.hooks.accelerate.call(10); // 异步钩子可以使用tapPromise、tapAsync、tap三种方式监听,触发的时候分别对应promise、callAsync、call myCar.hooks.calculateRoutes.tapPromise("GoogleMapsPlugin", (source, target, routesList) => { // return a promise return google.maps.findRoute(source, target).then(route => { routesList.add(route); }); }); myCar.hooks.calculateRoutes.tapAsync("BingMapsPlugin", (source, target, routesList, callback) => { bing.findRoute(source, target, (err, route) => { if(err) return callback(err); routesList.add(route); // call the callback callback(); }); }); myCar.hooks.calculateRoutes.tap("CachedRoutesPlugin", (source, target, routesList) => { const cachedRoute = cache.get(source, target); if(cachedRoute) routesList.add(cachedRoute); })
然后在跟着例子在使用的时候,比较难理解的一点是异步并行钩子的执行,自己想要了解具体是怎样实现的,所以直接看了下tapable的源码,具体去了解怎么注册监听及执行监听的、具体的钩子是怎么实现的
我们先已最简单的hook SyncHook来看
SyncHook.js class SyncHookCodeFactory extends HookCodeFactory { // new Function传入的代码串内this.content的具体实现,确保每个钩子可以定义实现身份钩子的代码 content({ onError, onDone, rethrowIfPossible }) { return this.callTapsSeries({ onError: (i, err) => onError(err), onDone, rethrowIfPossible }); } } const factory = new SyncHookCodeFactory(); // 最终的call、callAsync、promise函数 const COMPILE = function(options) { factory.setup(this, options); return factory.create(options); }; function SyncHook(args = [], name = undefined) { const hook = new Hook(args, name); hook.constructor = SyncHook; hook.tapAsync = TAP_ASYNC; hook.tapPromise = TAP_PROMISE; hook.compile = COMPILE; // new SyncHook 最终返回的是Hook的实例 return hook; }
Hook.js 定义基础Hook类,其它的类都继承自这个类,这个类定义了tap、tapPromise、tapAsync注册监听函数的方法,同时又定义了触发监听函数的call、callAsync、promise方法;需要注意的是call、callAsync、promise着三个方法都是调用_createCall方法创建,而_createCall方法内部返回的是this.compile的返回值,实际上每个Hook子类需要自己定义compile方法,而compile方法内又返回的是基类HookCodeFactory上create方法调用的返回值,而基类的create方法调会通过new Function生成一个fn,并返回这个fn,所以我们只要知道这个fn是怎么组织及调用的,我们就可以详细知道每个hook的调用过程,及返回值 const CALL_DELEGATE = function(...args) { this.call = this._createCall("sync"); return this.call(...args); }; const CALL_ASYNC_DELEGATE = function(...args) { this.callAsync = this._createCall("async"); return this.callAsync(...args); }; const PROMISE_DELEGATE = function(...args) { this.promise = this._createCall("promise"); return this.promise(...args); }; class Hook { constructor(args = [], name = undefined) { this._args = args; this.name = name; this.taps = []; this.interceptors = []; this._call = CALL_DELEGATE; this.call = CALL_DELEGATE; this._callAsync = CALL_ASYNC_DELEGATE; this.callAsync = CALL_ASYNC_DELEGATE; this._promise = PROMISE_DELEGATE; this.promise = PROMISE_DELEGATE; this._x = undefined; this.compile = this.compile; this.tap = this.tap; this.tapAsync = this.tapAsync; this.tapPromise = this.tapPromise; } compile(options) { throw new Error("Abstract: should be overridden"); } _createCall(type) { return this.compile({ taps: this.taps, interceptors: this.interceptors, args: this._args, type: type }); } _tap(type, options, fn) { // 格式化tap、tapAsync、tapPromise传入的参数 if (typeof options === "string") { options = { name: options.trim() }; } else if (typeof options !== "object" || options === null) { throw new Error("Invalid tap options"); } if (typeof options.name !== "string" || options.name === "") { throw new Error("Missing name for tap"); } // 注册上下文 if (typeof options.context !== "undefined") { deprecateContext(); } options = Object.assign({ type, fn }, options); // 注册拦截器 options = this._runRegisterInterceptors(options); this._insert(options); } tap(options, fn) { this._tap("sync", options, fn); } tapAsync(options, fn) { this._tap("async", options, fn); } tapPromise(options, fn) { this._tap("promise", options, fn); } _runRegisterInterceptors(options) { for (const interceptor of this.interceptors) { if (interceptor.register) { const newOptions = interceptor.register(options); if (newOptions !== undefined) { options = newOptions; } } } return options; } _insert(item) { // 根据传入的before、stage参数改变tap注册监听函数的属性,正常顺序是按照注册的顺序执行监听函数,同时也支持传入before、state这些参数改变监听函数的执行顺序 this._resetCompilation(); let before; if (typeof item.before === "string") { before = new Set([item.before]); } else if (Array.isArray(item.before)) { before = new Set(item.before); } let stage = 0; if (typeof item.stage === "number") { stage = item.stage; } let i = this.taps.length; while (i > 0) { i--; const x = this.taps[i]; this.taps[i + 1] = x; const xStage = x.stage || 0; if (before) { if (before.has(x.name)) { before.delete(x.name); continue; } if (before.size > 0) { continue; } } if (xStage > stage) { continue; } i++; break; } this.taps[i] = item; } }
HookCodeFactory.js class HookCodeFactory { constructor(config) { this.config = config; this.options = undefined; this._args = undefined; } // 通过new Function创建最终的call、callAsync、promise函数 // new Function ([arg1[, arg2[, ...argN]],] functionBody) create(options) { this.init(options); let fn; switch (this.options.type) { case "sync": fn = new Function( this.args(), '"use strict";\n' + this.header() + this.contentWithInterceptors({ onError: err => `throw ${err};\n`, onResult: result => `return ${result};\n`, resultReturns: true, onDone: () => "", rethrowIfPossible: true }) ); break; case "async": fn = new Function( this.args({ after: "_callback" }), '"use strict";\n' + this.header() + this.contentWithInterceptors({ onError: err => `_callback(${err});\n`, onResult: result => `_callback(null, ${result});\n`, onDone: () => "_callback();\n" }) ); break; case "promise": let errorHelperUsed = false; const content = this.contentWithInterceptors({ onError: err => { errorHelperUsed = true; return `_error(${err});\n`; }, onResult: result => `_resolve(${result});\n`, onDone: () => "_resolve();\n" }); let code = ""; code += '"use strict";\n'; code += this.header(); code += "return new Promise((function(_resolve, _reject) {\n"; if (errorHelperUsed) { code += "var _sync = true;\n"; code += "function _error(_err) {\n"; code += "if(_sync)\n"; code += "_resolve(Promise.resolve().then((function() { throw _err; })));\n"; code += "else\n"; code += "_reject(_err);\n"; code += "};\n"; } code += content; if (errorHelperUsed) { code += "_sync = false;\n"; } code += "}));\n"; fn = new Function(this.args(), code); break; } this.deinit(); return fn; } setup(instance, options) { instance._x = options.taps.map(t => t.fn); } init(options) { this.options = options; this._args = options.args.slice(); } deinit() { this.options = undefined; this._args = undefined; } contentWithInterceptors(options) { if (this.options.interceptors.length > 0) { const onError = options.onError; const onResult = options.onResult; const onDone = options.onDone; let code = ""; for (let i = 0; i < this.options.interceptors.length; i++) { const interceptor = this.options.interceptors[i]; if (interceptor.call) { code += `${this.getInterceptor(i)}.call(${this.args({ before: interceptor.context ? "_context" : undefined })});\n`; } } code += this.content( Object.assign(options, { onError: onError && (err => { let code = ""; for (let i = 0; i < this.options.interceptors.length; i++) { const interceptor = this.options.interceptors[i]; if (interceptor.error) { code += `${this.getInterceptor(i)}.error(${err});\n`; } } code += onError(err); return code; }), onResult: onResult && (result => { let code = ""; for (let i = 0; i < this.options.interceptors.length; i++) { const interceptor = this.options.interceptors[i]; if (interceptor.result) { code += `${this.getInterceptor(i)}.result(${result});\n`; } } code += onResult(result); return code; }), onDone: onDone && (() => { let code = ""; for (let i = 0; i < this.options.interceptors.length; i++) { const interceptor = this.options.interceptors[i]; if (interceptor.done) { code += `${this.getInterceptor(i)}.done();\n`; } } code += onDone(); return code; }) }) ); return code; } else { return this.content(options); } } header() { let code = ""; if (this.needContext()) { code += "var _context = {};\n"; } else { code += "var _context;\n"; } code += "var _x = this._x;\n"; if (this.options.interceptors.length > 0) { code += "var _taps = this.taps;\n"; code += "var _interceptors = this.interceptors;\n"; } return code; } needContext() { for (const tap of this.options.taps) if (tap.context) return true; return false; } callTap(tapIndex, { onError, onResult, onDone, rethrowIfPossible }) { let code = ""; let hasTapCached = false; for (let i = 0; i < this.options.interceptors.length; i++) { const interceptor = this.options.interceptors[i]; if (interceptor.tap) { if (!hasTapCached) { code += `var _tap${tapIndex} = ${this.getTap(tapIndex)};\n`; hasTapCached = true; } code += `${this.getInterceptor(i)}.tap(${ interceptor.context ? "_context, " : "" }_tap${tapIndex});\n`; } } code += `var _fn${tapIndex} = ${this.getTapFn(tapIndex)};\n`; const tap = this.options.taps[tapIndex]; switch (tap.type) { case "sync": if (!rethrowIfPossible) { code += `var _hasError${tapIndex} = false;\n`; code += "try {\n"; } if (onResult) { code += `var _result${tapIndex} = _fn${tapIndex}(${this.args({ before: tap.context ? "_context" : undefined })});\n`; } else { code += `_fn${tapIndex}(${this.args({ before: tap.context ? "_context" : undefined })});\n`; } if (!rethrowIfPossible) { code += "} catch(_err) {\n"; code += `_hasError${tapIndex} = true;\n`; code += onError("_err"); code += "}\n"; code += `if(!_hasError${tapIndex}) {\n`; } if (onResult) { code += onResult(`_result${tapIndex}`); } if (onDone) { code += onDone(); } if (!rethrowIfPossible) { code += "}\n"; } break; case "async": let cbCode = ""; if (onResult) cbCode += `(function(_err${tapIndex}, _result${tapIndex}) {\n`; else cbCode += `(function(_err${tapIndex}) {\n`; cbCode += `if(_err${tapIndex}) {\n`; cbCode += onError(`_err${tapIndex}`); cbCode += "} else {\n"; if (onResult) { cbCode += onResult(`_result${tapIndex}`); } if (onDone) { cbCode += onDone(); } cbCode += "}\n"; cbCode += "})"; code += `_fn${tapIndex}(${this.args({ before: tap.context ? "_context" : undefined, after: cbCode })});\n`; break; case "promise": code += `var _hasResult${tapIndex} = false;\n`; code += `var _promise${tapIndex} = _fn${tapIndex}(${this.args({ before: tap.context ? "_context" : undefined })});\n`; code += `if (!_promise${tapIndex} || !_promise${tapIndex}.then)\n`; code += ` throw new Error('Tap function (tapPromise) did not return promise (returned ' + _promise${tapIndex} + ')');\n`; code += `_promise${tapIndex}.then((function(_result${tapIndex}) {\n`; code += `_hasResult${tapIndex} = true;\n`; if (onResult) { code += onResult(`_result${tapIndex}`); } if (onDone) { code += onDone(); } code += `}), function(_err${tapIndex}) {\n`; code += `if(_hasResult${tapIndex}) throw _err${tapIndex};\n`; code += onError(`_err${tapIndex}`); code += "});\n"; break; } return code; } callTapsSeries({ onError, onResult, resultReturns, onDone, doneReturns, rethrowIfPossible }) { if (this.options.taps.length === 0) return onDone(); const firstAsync = this.options.taps.findIndex(t => t.type !== "sync"); const somethingReturns = resultReturns || doneReturns; let code = ""; let current = onDone; let unrollCounter = 0; for (let j = this.options.taps.length - 1; j >= 0; j--) { const i = j; const unroll = current !== onDone && (this.options.taps[i].type !== "sync" || unrollCounter++ > 20); if (unroll) { unrollCounter = 0; code += `function _next${i}() {\n`; code += current(); code += `}\n`; current = () => `${somethingReturns ? "return " : ""}_next${i}();\n`; } const done = current; const doneBreak = skipDone => { if (skipDone) return ""; return onDone(); }; const content = this.callTap(i, { onError: error => onError(i, error, done, doneBreak), onResult: onResult && (result => { return onResult(i, result, done, doneBreak); }), onDone: !onResult && done, rethrowIfPossible: rethrowIfPossible && (firstAsync < 0 || i < firstAsync) }); current = () => content; } code += current(); return code; } callTapsLooping({ onError, onDone, rethrowIfPossible }) { if (this.options.taps.length === 0) return onDone(); const syncOnly = this.options.taps.every(t => t.type === "sync"); let code = ""; if (!syncOnly) { code += "var _looper = (function() {\n"; code += "var _loopAsync = false;\n"; } code += "var _loop;\n"; code += "do {\n"; code += "_loop = false;\n"; for (let i = 0; i < this.options.interceptors.length; i++) { const interceptor = this.options.interceptors[i]; if (interceptor.loop) { code += `${this.getInterceptor(i)}.loop(${this.args({ before: interceptor.context ? "_context" : undefined })});\n`; } } code += this.callTapsSeries({ onError, onResult: (i, result, next, doneBreak) => { let code = ""; code += `if(${result} !== undefined) {\n`; code += "_loop = true;\n"; if (!syncOnly) code += "if(_loopAsync) _looper();\n"; code += doneBreak(true); code += `} else {\n`; code += next(); code += `}\n`; return code; }, onDone: onDone && (() => { let code = ""; code += "if(!_loop) {\n"; code += onDone(); code += "}\n"; return code; }), rethrowIfPossible: rethrowIfPossible && syncOnly }); code += "} while(_loop);\n"; if (!syncOnly) { code += "_loopAsync = true;\n"; code += "});\n"; code += "_looper();\n"; } return code; } callTapsParallel({ onError, onResult, onDone, rethrowIfPossible, onTap = (i, run) => run() }) { if (this.options.taps.length <= 1) { return this.callTapsSeries({ onError, onResult, onDone, rethrowIfPossible }); } let code = ""; code += "do {\n"; code += `var _counter = ${this.options.taps.length};\n`; if (onDone) { code += "var _done = (function() {\n"; code += onDone(); code += "});\n"; } for (let i = 0; i < this.options.taps.length; i++) { const done = () => { if (onDone) return "if(--_counter === 0) _done();\n"; else return "--_counter;"; }; const doneBreak = skipDone => { if (skipDone || !onDone) return "_counter = 0;\n"; else return "_counter = 0;\n_done();\n"; }; code += "if(_counter <= 0) break;\n"; code += onTap( i, () => this.callTap(i, { onError: error => { let code = ""; code += "if(_counter > 0) {\n"; code += onError(i, error, done, doneBreak); code += "}\n"; return code; }, onResult: onResult && (result => { let code = ""; code += "if(_counter > 0) {\n"; code += onResult(i, result, done, doneBreak); code += "}\n"; return code; }), onDone: !onResult && (() => { return done(); }), rethrowIfPossible }), done, doneBreak ); } code += "} while(false);\n"; return code; } args({ before, after } = {}) { let allArgs = this._args; if (before) allArgs = [before].concat(allArgs); if (after) allArgs = allArgs.concat(after); if (allArgs.length === 0) { return ""; } else { return allArgs.join(", "); } } getTapFn(idx) { return `_x[${idx}]`; } getTap(idx) { return `_taps[${idx}]`; } getInterceptor(idx) { return `_interceptors[${idx}]`; } }
我们用一个例子与源码来看下,最终输出的
class Car { constructor() { this.hooks = { // new SyncHook 传入的参数,就是定义call的时候需要传几个参数,然后tap的时候可以接受call传入的几个参数,所以要想tap的时候能过接收到参数,call的时候传入的参数有效,则实例化的时候需要传入参数 syncHook: new SyncHook(["SyncHookarg1", "SyncHookarg2", "SyncHookarg3"]), syncBailHook: new SyncBailHook(["syncBailHookarg1", "syncBailHookarg2", "syncBailHookarg3"]), syncWaterfallHook: new SyncWaterfallHook(["syncWaterfallHookarg1", "syncWaterfallHookarg2", "syncWaterfallHookarg3"]), syncLoopHook: new SyncLoopHook(["syncLoopHookarg1", "syncLoopHookarg2", "syncLoopHookarg3"]), asyncParallelHook: new AsyncParallelHook(["asyncParallelHookarg1", "asyncParallelHookarg2", "asyncParallelHookarg3"]), asyncParallelBailHook: new AsyncParallelBailHook(["asyncParallelBailHookarg1", "asyncParallelBailHookarg2", "asyncParallelBailHookarg3"]), asyncSeriesHook: new AsyncSeriesHook(["asyncSeriesHookarg1", "asyncSeriesHookarg2", "asyncSeriesHookarg3"]), asyncSeriesBailHook: new AsyncSeriesBailHook(["asyncSeriesBailHookarg1", "asyncSeriesBailHookarg2", "asyncSeriesBailHookarg3"]), asyncSeriesWaterfallHook: new AsyncSeriesWaterfallHook(["asyncSeriesWaterfallHookarg1", "asyncSeriesWaterfallHookarg2", "asyncSeriesWaterfallHookarg3"]), } } } // 同步钩子例子,每次需要更改对应的属性key即可测试验证 car.hooks.syncHook.tap('start', (...args) => { console.log('start', ...args) // return 没有意义 return undefined }) car.hooks.syncHook.tap('speed', (...args) => { // throw new Error('speed') let i = 0 while(i < 2000000000) { i += 1 } console.log('speed', ...args) return undefined }) car.hooks.syncHook.tap('stop', (...args) => { console.log('stop', ...args) return undefined }) car.hooks.syncHook.tap('end', (...args) => { console.log('end', ...args) return undefined }) const syncResult = car.hooks.syncHook.call(1, 2, 3) // 异步 tapAsync注册的钩子 car.hooks.asyncParallelBailHook.tapAsync('start', (...args) => { console.log('start', ...(args.slice(0, 3))) const callback = args[args.length - 1] return callback() }) car.hooks.asyncParallelBailHook.tapAsync('speed', (...args) => { let i = 0 while(i < 2000000000) { i += 1 } console.log('speed', ...(args.slice(0, 3))) const callback = args[args.length - 1] return callback(null, '77') }) car.hooks.asyncParallelBailHook.tapAsync('stop', (...args) => { console.log('stop', ...(args.slice(0, 3))) const callback = args[args.length - 1] return callback(new Error('stop'), '66') }) car.hooks.asyncParallelBailHook.tapAsync('end', (...args) => { console.log('end', ...(args.slice(0, 3))) const callback = args[args.length - 1] return callback(null, '55') }) car.hooks.asyncParallelBailHook.callAsync(1, 2, 3, (error, result) => { console.log('async result', error, result) }) // 异步 tapPromise注册的钩子 car.hooks.asyncParallelHook.tapPromise('start', (...args) => { console.log('start', ...(args.slice(0, 3))) return new Promise((resolve) => { setTimeout(() => { resolve(undefined) }, 5000) }) }) car.hooks.asyncParallelHook.tapPromise('speed', (...args) => { let i = 0 while(i < 2000000000) { i += 1 } console.log('speed', ...(args.slice(0, 3))) // return callback('77') return Promise.resolve(77) }) car.hooks.asyncParallelHook.tapPromise('stop', (...args) => { console.log('stop', ...(args.slice(0, 3))) // return callback('66') return Promise.reject(new Error('stop')) }) car.hooks.asyncParallelHook.tapPromise('end', (...args) => { console.log('end', ...(args.slice(0, 3))) // return callback('55') return Promise.resolve(55) }) car.hooks.asyncParallelHook.promise(1, 2, 3).then((result) => { console.log('async promise result', result) return result }).catch((err) => { console.log('async promise err', err.message) })
syncHook
输出new Function中传入的BodyCode部分 "use strict"; var _context; var _x = this._x; var _fn0 = _x[0]; _fn0(SyncHookarg1, SyncHookarg2, SyncHookarg3); var _fn1 = _x[1]; _fn1(SyncHookarg1, SyncHookarg2, SyncHookarg3); var _fn2 = _x[2]; _fn2(SyncHookarg1, SyncHookarg2, SyncHookarg3); var _fn3 = _x[3]; _fn3(SyncHookarg1, SyncHookarg2, SyncHookarg3);
从上面最终生成的BodyCode来看,按照taps顺序执行,其中一个出错,后续会中断执行,最终call的返回值为undefined
syncBailHook
"use strict"; var _context; var _x = this._x; var _fn0 = _x[0]; var _result0 = _fn0(syncBailHookarg1, syncBailHookarg2, syncBailHookarg3); if (_result0 !== undefined) { return _result0;; } else { var _fn1 = _x[1]; var _result1 = _fn1(syncBailHookarg1, syncBailHookarg2, syncBailHookarg3); if (_result1 !== undefined) { return _result1;; } else { var _fn2 = _x[2]; var _result2 = _fn2(syncBailHookarg1, syncBailHookarg2, syncBailHookarg3); if (_result2 !== undefined) { return _result2;; } else { var _fn3 = _x[3]; var _result3 = _fn3(syncBailHookarg1, syncBailHookarg2, syncBailHookarg3); if (_result3 !== undefined) { return _result3;; } else {} } } }
从上面最终生成的BodyCode来看,按照taps顺序执行,其中一个出错,后续会中断执行,当其中一个监听函数有返回值(返回值不能是undefined 或者 undefined字符串),则后续的监听函数不会在执行,call的返回值为return的值
syncWaterfallHook
"use strict"; var _context; var _x = this._x; var _fn0 = _x[0]; var _result0 = _fn0(syncWaterfallHookarg1, syncWaterfallHookarg2, syncWaterfallHookarg3); if (_result0 !== undefined) { syncWaterfallHookarg1 = _result0; } var _fn1 = _x[1]; var _result1 = _fn1(syncWaterfallHookarg1, syncWaterfallHookarg2, syncWaterfallHookarg3); if (_result1 !== undefined) { syncWaterfallHookarg1 = _result1; } var _fn2 = _x[2]; var _result2 = _fn2(syncWaterfallHookarg1, syncWaterfallHookarg2, syncWaterfallHookarg3); if (_result2 !== undefined) { syncWaterfallHookarg1 = _result2; } var _fn3 = _x[3]; var _result3 = _fn3(syncWaterfallHookarg1, syncWaterfallHookarg2, syncWaterfallHookarg3); if (_result3 !== undefined) { syncWaterfallHookarg1 = _result3; } return syncWaterfallHookarg1;
从上面最终生成的BodyCode来看,按照taps顺序执行,其中一个出错,后续会中断执行,前一个监听函数返回的值不为undefined则会被当成第一个参数传入到下一个监听函数,call最后的返回值是最后一个监听函数的return的值
syncLoopHook
"use strict"; var _context; var _x = this._x; var _loop; do { _loop = false; var _fn0 = _x[0]; var _result0 = _fn0(syncLoopHookarg1, syncLoopHookarg2, syncLoopHookarg3); if (_result0 !== undefined) { _loop = true; } else { var _fn1 = _x[1]; var _result1 = _fn1(syncLoopHookarg1, syncLoopHookarg2, syncLoopHookarg3); if (_result1 !== undefined) { _loop = true; } else { var _fn2 = _x[2]; var _result2 = _fn2(syncLoopHookarg1, syncLoopHookarg2, syncLoopHookarg3); if (_result2 !== undefined) { _loop = true; } else { var _fn3 = _x[3]; var _result3 = _fn3(syncLoopHookarg1, syncLoopHookarg2, syncLoopHookarg3); if (_result3 !== undefined) { _loop = true; } else { if (!_loop) {} } } } } } while (_loop);
从上面最终生成的BodyCode来看,按照taps顺序执行,其中一个出错,后续会中断执行,按顺序执行的过程中,监听函数没有返回undefined,则会按照顺序重新执行,直到所有的监听函数都返回undefined,call最终的返回值为undefiend
asyncSeriesHook tapPromise promise
"use strict"; var _context; var _x = this._x; return new Promise((function (_resolve, _reject) { var _sync = true; function _error(_err) { if (_sync) _resolve(Promise.resolve().then((function () { throw _err; }))); else _reject(_err); }; function _next2() { var _fn3 = _x[3]; var _hasResult3 = false; var _promise3 = _fn3(asyncSeriesHookarg1, asyncSeriesHookarg2, asyncSeriesHookarg3); if (!_promise3 || !_promise3.then) throw new Error('Tap function (tapPromise) did not return promise (returned ' + _promise3 + ')'); _promise3.then((function (_result3) { _hasResult3 = true; _resolve(); }), function (_err3) { if (_hasResult3) throw _err3; _error(_err3); }); } function _next1() { var _fn2 = _x[2]; var _hasResult2 = false; var _promise2 = _fn2(asyncSeriesHookarg1, asyncSeriesHookarg2, asyncSeriesHookarg3); 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; _error(_err2); }); } function _next0() { var _fn1 = _x[1]; var _hasResult1 = false; var _promise1 = _fn1(asyncSeriesHookarg1, asyncSeriesHookarg2, asyncSeriesHookarg3); if (!_promise1 || !_promise1.then) throw new Error('Tap function (tapPromise) did not return promise (returned ' + _promise1 + ')'); _promise1.then((function (_result1) { _hasResult1 = true; _next1(); }), function (_err1) { if (_hasResult1) throw _err1; _error(_err1); }); } var _fn0 = _x[0]; var _hasResult0 = false; var _promise0 = _fn0(asyncSeriesHookarg1, asyncSeriesHookarg2, asyncSeriesHookarg3); if (!_promise0 || !_promise0.then) throw new Error('Tap function (tapPromise) did not return promise (returned ' + _promise0 + ')'); _promise0.then((function (_result0) { _hasResult0 = true; _next0(); }), function (_err0) { if (_hasResult0) throw _err0; _error(_err0); }); _sync = false; }));
从上面最终生成的BodyCode来看,没有最终返回值,按照顺序执行,只要其中一个抛出错误,中断后续执行,并进入catch捕获函数
asyncSeriesHook tapAsync callAsync
"use strict"; var _context; var _x = this._x; function _next2() { var _fn3 = _x[3]; _fn3(asyncSeriesHookarg1, asyncSeriesHookarg2, asyncSeriesHookarg3, (function (_err3) { if (_err3) { _callback(_err3); } else { _callback(); } })); } function _next1() { var _fn2 = _x[2]; _fn2(asyncSeriesHookarg1, asyncSeriesHookarg2, asyncSeriesHookarg3, (function (_err2) { if (_err2) { _callback(_err2); } else { _next2(); } })); } function _next0() { var _fn1 = _x[1]; _fn1(asyncSeriesHookarg1, asyncSeriesHookarg2, asyncSeriesHookarg3, (function (_err1) { if (_err1) { _callback(_err1); } else { _next1(); } })); } var _fn0 = _x[0]; _fn0(asyncSeriesHookarg1, asyncSeriesHookarg2, asyncSeriesHookarg3, (function (_err0) { if (_err0) { _callback(_err0); } else { _next0(); } }));
从上面最终生成的BodyCode来看, 没有最终返回值,按照taps顺序执行,只要其中一个抛出错误,中断后续执行,并执行callAsync传入的callback函数,第一个参数为接受到的错误,与promise区别的是,每个tapAsync传入的最后一个参数是一个next函数,改函数接受一个错误参数,如果传入该参数则直接调用callAsync的回调callback
对于asyncSeriesHook钩子 tapAsync、tapPromise效果一致
asyncSeriesBailHook tapPromise promise
"use strict"; var _context; var _x = this._x; return new Promise((function (_resolve, _reject) { var _sync = true; function _error(_err) { if (_sync) _resolve(Promise.resolve().then((function () { throw _err; }))); else _reject(_err); }; function _next2() { var _fn3 = _x[3]; var _hasResult3 = false; var _promise3 = _fn3(asyncSeriesBailHookarg1, asyncSeriesBailHookarg2, asyncSeriesBailHookarg3); 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) { _resolve(_result3); } else { _resolve(); } }), function (_err3) { if (_hasResult3) throw _err3; _error(_err3); }); } function _next1() { var _fn2 = _x[2]; var _hasResult2 = false; var _promise2 = _fn2(asyncSeriesBailHookarg1, asyncSeriesBailHookarg2, asyncSeriesBailHookarg3); 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) { _resolve(_result2); } else { _next2(); } }), function (_err2) { if (_hasResult2) throw _err2; _error(_err2); }); } function _next0() { var _fn1 = _x[1]; var _hasResult1 = false; var _promise1 = _fn1(asyncSeriesBailHookarg1, asyncSeriesBailHookarg2, asyncSeriesBailHookarg3); if (!_promise1 || !_promise1.then) throw new Error('Tap function (tapPromise) did not return promise (returned ' + _promise1 + ')'); _promise1.then((function (_result1) { _hasResult1 = true; if (_result1 !== undefined) { _resolve(_result1); } else { _next1(); } }), function (_err1) { if (_hasResult1) throw _err1; _error(_err1); }); } var _fn0 = _x[0]; var _hasResult0 = false; var _promise0 = _fn0(asyncSeriesBailHookarg1, asyncSeriesBailHookarg2, asyncSeriesBailHookarg3); if (!_promise0 || !_promise0.then) throw new Error('Tap function (tapPromise) did not return promise (returned ' + _promise0 + ')'); _promise0.then((function (_result0) { _hasResult0 = true; if (_result0 !== undefined) { _resolve(_result0); } else { _next0(); } }), function (_err0) { if (_hasResult0) throw _err0; _error(_err0); }); _sync = false; }));
从上面最终生成的BodyCode来看, 按照taps顺序执行,如果监听函数有返回值,则直接resolve,后续的监听函数不在执行,如果抛出错误与asyncSeriesHook钩子一样,通过catch函数捕获
asyncSeriesBailHook tapAsync callAsync
"use strict"; var _context; var _x = this._x; function _next2() { var _fn3 = _x[3]; _fn3(asyncSeriesBailHookarg1, asyncSeriesBailHookarg2, asyncSeriesBailHookarg3, (function (_err3, _result3) { if (_err3) { _callback(_err3); } else { if (_result3 !== undefined) { _callback(null, _result3); } else { _callback(); } } })); } function _next1() { var _fn2 = _x[2]; _fn2(asyncSeriesBailHookarg1, asyncSeriesBailHookarg2, asyncSeriesBailHookarg3, (function (_err2, _result2) { if (_err2) { _callback(_err2); } else { if (_result2 !== undefined) { _callback(null, _result2); } else { _next2(); } } })); } function _next0() { var _fn1 = _x[1]; _fn1(asyncSeriesBailHookarg1, asyncSeriesBailHookarg2, asyncSeriesBailHookarg3, (function (_err1, _result1) { if (_err1) { _callback(_err1); } else { if (_result1 !== undefined) { _callback(null, _result1); } else { _next1(); } } })); } var _fn0 = _x[0]; _fn0(asyncSeriesBailHookarg1, asyncSeriesBailHookarg2, asyncSeriesBailHookarg3, (function (_err0, _result0) { if (_err0) { _callback(_err0); } else { if (_result0 !== undefined) { _callback(null, _result0); } else { _next0(); } } }));
从上面最终生成的BodyCode来看, 按照taps顺序执行,如果监听函数有返回值,则直接调用callAsync传入的callback,并将返回的结果传入第二个参数,后续的监听函数不在执行,如果抛出错误则直接调用callAsync传入的callback并将错误信息传入第一个参数
asyncSeriesBailHook tapPromise 与 tapAsync 效果一致
asyncSeriesWaterfallHook tapPromise promise
"use strict"; var _context; var _x = this._x; return new Promise((function (_resolve, _reject) { var _sync = true; function _error(_err) { if (_sync) _resolve(Promise.resolve().then((function () { throw _err; }))); else _reject(_err); }; function _next2() { var _fn3 = _x[3]; var _hasResult3 = false; var _promise3 = _fn3(asyncSeriesWaterfallHookarg1, asyncSeriesWaterfallHookarg2, asyncSeriesWaterfallHookarg3); 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) { asyncSeriesWaterfallHookarg1 = _result3; } _resolve(asyncSeriesWaterfallHookarg1); }), function (_err3) { if (_hasResult3) throw _err3; _error(_err3); }); } function _next1() { var _fn2 = _x[2]; var _hasResult2 = false; var _promise2 = _fn2(asyncSeriesWaterfallHookarg1, asyncSeriesWaterfallHookarg2, asyncSeriesWaterfallHookarg3); 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) { asyncSeriesWaterfallHookarg1 = _result2; } _next2(); }), function (_err2) { if (_hasResult2) throw _err2; _error(_err2); }); } function _next0() { var _fn1 = _x[1]; var _hasResult1 = false; var _promise1 = _fn1(asyncSeriesWaterfallHookarg1, asyncSeriesWaterfallHookarg2, asyncSeriesWaterfallHookarg3); if (!_promise1 || !_promise1.then) throw new Error('Tap function (tapPromise) did not return promise (returned ' + _promise1 + ')'); _promise1.then((function (_result1) { _hasResult1 = true; if (_result1 !== undefined) { asyncSeriesWaterfallHookarg1 = _result1; } _next1(); }), function (_err1) { if (_hasResult1) throw _err1; _error(_err1); }); } var _fn0 = _x[0]; var _hasResult0 = false; var _promise0 = _fn0(asyncSeriesWaterfallHookarg1, asyncSeriesWaterfallHookarg2, asyncSeriesWaterfallHookarg3); if (!_promise0 || !_promise0.then) throw new Error('Tap function (tapPromise) did not return promise (returned ' + _promise0 + ')'); _promise0.then((function (_result0) { _hasResult0 = true; if (_result0 !== undefined) { asyncSeriesWaterfallHookarg1 = _result0; } _next0(); }), function (_err0) { if (_hasResult0) throw _err0; _error(_err0); }); _sync = false; }));
从上面最终生成的BodyCode来看, 按照taps顺序执行,如果监听函数有返回值,则直接resolve,并将resolve的值替换第一个参数并传入到下一个监听函数,最终resolve出去的值为,最后一个监听函数resolve的值,如果抛出错误则中断执行,通过catch函数捕获
asyncSeriesWaterfallHook tapAsync callAsync
"use strict"; var _context; var _x = this._x; function _next2() { var _fn3 = _x[3]; _fn3(asyncSeriesWaterfallHookarg1, asyncSeriesWaterfallHookarg2, asyncSeriesWaterfallHookarg3, (function (_err3, _result3) { if (_err3) { _callback(_err3); } else { if (_result3 !== undefined) { asyncSeriesWaterfallHookarg1 = _result3; } _callback(null, asyncSeriesWaterfallHookarg1); } })); } function _next1() { var _fn2 = _x[2]; _fn2(asyncSeriesWaterfallHookarg1, asyncSeriesWaterfallHookarg2, asyncSeriesWaterfallHookarg3, (function (_err2, _result2) { if (_err2) { _callback(_err2); } else { if (_result2 !== undefined) { asyncSeriesWaterfallHookarg1 = _result2; } _next2(); } })); } function _next0() { var _fn1 = _x[1]; _fn1(asyncSeriesWaterfallHookarg1, asyncSeriesWaterfallHookarg2, asyncSeriesWaterfallHookarg3, (function (_err1, _result1) { if (_err1) { _callback(_err1); } else { if (_result1 !== undefined) { asyncSeriesWaterfallHookarg1 = _result1; } _next1(); } })); } var _fn0 = _x[0]; _fn0(asyncSeriesWaterfallHookarg1, asyncSeriesWaterfallHookarg2, asyncSeriesWaterfallHookarg3, (function (_err0, _result0) { if (_err0) { _callback(_err0); } else { if (_result0 !== undefined) { asyncSeriesWaterfallHookarg1 = _result0; } _next0(); } }));
从上面最终生成的BodyCode来看, 按照taps顺序执行,如果监听函数有返回值,则调用next,并return的值替换第一个参数并传入到下一个监听函数,最终return出去的值为,最后一个监听函数return的值,如果抛出错误则中断执行,并在callAsync的callback内接受到第一个参数,值为错误信息
asyncSeriesWaterfallHook tapPromise 与 tapAsync的效果一致
asyncParallelHook tapPromise promise
"use strict"; var _context; var _x = this._x; return new Promise((function (_resolve, _reject) { var _sync = true; function _error(_err) { if (_sync) _resolve(Promise.resolve().then((function () { throw _err; }))); else _reject(_err); }; do { var _counter = 4; var _done = (function () { _resolve(); }); if (_counter <= 0) break; var _fn0 = _x[0]; var _hasResult0 = false; var _promise0 = _fn0(asyncParallelHookarg1, asyncParallelHookarg2, asyncParallelHookarg3); if (!_promise0 || !_promise0.then) throw new Error('Tap function (tapPromise) did not return promise (returned ' + _promise0 + ')'); _promise0.then((function (_result0) { _hasResult0 = true; if (--_counter === 0) _done(); }), function (_err0) { if (_hasResult0) throw _err0; if (_counter > 0) { _error(_err0); _counter = 0; } }); if (_counter <= 0) break; var _fn1 = _x[1]; var _hasResult1 = false; var _promise1 = _fn1(asyncParallelHookarg1, asyncParallelHookarg2, asyncParallelHookarg3); if (!_promise1 || !_promise1.then) throw new Error('Tap function (tapPromise) did not return promise (returned ' + _promise1 + ')'); _promise1.then((function (_result1) { _hasResult1 = true; if (--_counter === 0) _done(); }), function (_err1) { if (_hasResult1) throw _err1; if (_counter > 0) { _error(_err1); _counter = 0; } }); if (_counter <= 0) break; var _fn2 = _x[2]; var _hasResult2 = false; var _promise2 = _fn2(asyncParallelHookarg1, asyncParallelHookarg2, asyncParallelHookarg3); 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) { _error(_err2); _counter = 0; } }); if (_counter <= 0) break; var _fn3 = _x[3]; var _hasResult3 = false; var _promise3 = _fn3(asyncParallelHookarg1, asyncParallelHookarg2, asyncParallelHookarg3); 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) { _error(_err3); _counter = 0; } }); } while (false); _sync = false; }));
从上面最终生成的BodyCode来看, 按照taps生成对应的promise,每个promise都会执行,当任何一个promise先抛出错误,则会导致最终执行的promise()进入catch
asyncParallel tapAsync callAsync
"use strict"; var _context; var _x = this._x; do { var _counter = 4; var _done = (function () { _callback(); }); if (_counter <= 0) break; var _fn0 = _x[0]; _fn0(asyncParallelHookarg1, asyncParallelHookarg2, asyncParallelHookarg3, (function (_err0) { if (_err0) { if (_counter > 0) { _callback(_err0); _counter = 0; } } else { if (--_counter === 0) _done(); } })); if (_counter <= 0) break; var _fn1 = _x[1]; _fn1(asyncParallelHookarg1, asyncParallelHookarg2, asyncParallelHookarg3, (function (_err1) { if (_err1) { if (_counter > 0) { _callback(_err1); _counter = 0; } } else { if (--_counter === 0) _done(); } })); if (_counter <= 0) break; var _fn2 = _x[2]; _fn2(asyncParallelHookarg1, asyncParallelHookarg2, asyncParallelHookarg3, (function (_err2) { if (_err2) { if (_counter > 0) { _callback(_err2); _counter = 0; } } else { if (--_counter === 0) _done(); } })); if (_counter <= 0) break; var _fn3 = _x[3]; _fn3(asyncParallelHookarg1, asyncParallelHookarg2, asyncParallelHookarg3, (function (_err3) { if (_err3) { if (_counter > 0) { _callback(_err3); _counter = 0; } } else { if (--_counter === 0) _done(); } })); } while (false)
从上面最终生成的BodyCode来看, 按照taps调用对应的监听函数,当任何一个监听函数先抛出错误,则会导致后面的监听函数不会执行,且会触发callAsync传入的callback,并将错误参数当成第一个参数传入;注意这里与tapPromise注册方式有区别,区别在tapPromise注册的监听函数就算报错其它的监听函数都会执行,而tapAsync方式注册当其中有一个监听函数抛出错误,后续的就不执行了,原因在于tapPromise是在then方法内将_counter=0,而then是microtask,所以后面的promise会继续执行
asyncParallelHook tapPromise 与 tapAsync 当有错误抛出时,二者的表现是有区别的
asyncParallelBailHook tapPromise promise
"use strict"; var _context; var _x = this._x; return new Promise((function (_resolve, _reject) { var _sync = true; function _error(_err) { if (_sync) _resolve(Promise.resolve().then((function () { throw _err; }))); else _reject(_err); }; var _results = new Array(4); var _checkDone = function () { for (var i = 0; i < _results.length; i++) { var item = _results[i]; if (item === undefined) return false; if (item.result !== undefined) { _resolve(item.result); return true; } if (item.error) { _error(item.error); return true; } } return false; } do { var _counter = 4; var _done = (function () { _resolve(); }); if (_counter <= 0) break; var _fn0 = _x[0]; var _hasResult0 = false; var _promise0 = _fn0(asyncParallelBailHookarg1, asyncParallelBailHookarg2, asyncParallelBailHookarg3); if (!_promise0 || !_promise0.then) throw new Error('Tap function (tapPromise) did not return promise (returned ' + _promise0 + ')'); _promise0.then((function (_result0) { _hasResult0 = true; if (_counter > 0) { if (0 < _results.length && (_result0 !== undefined && (_results.length = 1), (_results[0] = { result: _result0 }), _checkDone())) { _counter = 0; } else { if (--_counter === 0) _done(); } } }), function (_err0) { if (_hasResult0) throw _err0; if (_counter > 0) { if (0 < _results.length && ((_results.length = 1), (_results[0] = { error: _err0 }), _checkDone())) { _counter = 0; } else { if (--_counter === 0) _done(); } } }); if (_counter <= 0) break; if (1 >= _results.length) { if (--_counter === 0) _done(); } else { var _fn1 = _x[1]; var _hasResult1 = false; var _promise1 = _fn1(asyncParallelBailHookarg1, asyncParallelBailHookarg2, asyncParallelBailHookarg3); if (!_promise1 || !_promise1.then) throw new Error('Tap function (tapPromise) did not return promise (returned ' + _promise1 + ')'); _promise1.then((function (_result1) { _hasResult1 = true; if (_counter > 0) { if (1 < _results.length && (_result1 !== undefined && (_results.length = 2), (_results[1] = { result: _result1 }), _checkDone())) { _counter = 0; } else { if (--_counter === 0) _done(); } } }), function (_err1) { if (_hasResult1) throw _err1; if (_counter > 0) { if (1 < _results.length && ((_results.length = 2), (_results[1] = { error: _err1 }), _checkDone())) { _counter = 0; } else { if (--_counter === 0) _done(); } } }); } if (_counter <= 0) break; if (2 >= _results.length) { if (--_counter === 0) _done(); } else { var _fn2 = _x[2]; var _hasResult2 = false; var _promise2 = _fn2(asyncParallelBailHookarg1, asyncParallelBailHookarg2, asyncParallelBailHookarg3); 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) { if (2 < _results.length && (_result2 !== undefined && (_results.length = 3), (_results[2] = { result: _result2 }), _checkDone())) { _counter = 0; } else { if (--_counter === 0) _done(); } } }), function (_err2) { if (_hasResult2) throw _err2; if (_counter > 0) { if (2 < _results.length && ((_results.length = 3), (_results[2] = { error: _err2 }), _checkDone())) { _counter = 0; } else { if (--_counter === 0) _done(); } } }); } if (_counter <= 0) break; if (3 >= _results.length) { if (--_counter === 0) _done(); } else { var _fn3 = _x[3]; var _hasResult3 = false; var _promise3 = _fn3(asyncParallelBailHookarg1, asyncParallelBailHookarg2, asyncParallelBailHookarg3); 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) { if (3 < _results.length && (_result3 !== undefined && (_results.length = 4), (_results[3] = { result: _result3 }), _checkDone())) { _counter = 0; } else { if (--_counter === 0) _done(); } } }), function (_err3) { if (_hasResult3) throw _err3; if (_counter > 0) { if (3 < _results.length && ((_results.length = 4), (_results[3] = { error: _err3 }), _checkDone())) { _counter = 0; } else { if (--_counter === 0) _done(); } } }); } } while (false); _sync = false; }));
从上面最终生成的BodyCode来看, 按照taps生成对应的promise,然后陆续执行promise,当其中一个promise先返回值,还得确认该监听函数在taps中的顺序,如果顺序也满足,则会进入promise()的then回调,并将返回值返回出去,如果不满足顺序,则会等待前面的监听函数resolve,如果有一个promise抛出错误且还未有其它promise先成功,则会进入promise()的catch回调
asyncParallelBailHook tapAsync callAsync
"use strict"; var _context; var _x = this._x; var _results = new Array(4); var _checkDone = function () { for (var i = 0; i < _results.length; i++) { var item = _results[i]; if (item === undefined) return false; if (item.result !== undefined) { _callback(null, item.result); return true; } if (item.error) { _callback(item.error); return true; } } return false; } do { var _counter = 4; var _done = (function () { _callback(); }); if (_counter <= 0) break; var _fn0 = _x[0]; _fn0(asyncParallelBailHookarg1, asyncParallelBailHookarg2, asyncParallelBailHookarg3, (function (_err0, _result0) { if (_err0) { if (_counter > 0) { if (0 < _results.length && ((_results.length = 1), (_results[0] = { error: _err0 }), _checkDone())) { _counter = 0; } else { if (--_counter === 0) _done(); } } } else { if (_counter > 0) { if (0 < _results.length && (_result0 !== undefined && (_results.length = 1), (_results[0] = { result: _result0 }), _checkDone())) { _counter = 0; } else { if (--_counter === 0) _done(); } } } })); if (_counter <= 0) break; if (1 >= _results.length) { if (--_counter === 0) _done(); } else { var _fn1 = _x[1]; _fn1(asyncParallelBailHookarg1, asyncParallelBailHookarg2, asyncParallelBailHookarg3, (function (_err1, _result1) { if (_err1) { if (_counter > 0) { if (1 < _results.length && ((_results.length = 2), (_results[1] = { error: _err1 }), _checkDone())) { _counter = 0; } else { if (--_counter === 0) _done(); } } } else { if (_counter > 0) { if (1 < _results.length && (_result1 !== undefined && (_results.length = 2), (_results[1] = { result: _result1 }), _checkDone())) { _counter = 0; } else { if (--_counter === 0) _done(); } } } })); } if (_counter <= 0) break; if (2 >= _results.length) { if (--_counter === 0) _done(); } else { var _fn2 = _x[2]; _fn2(asyncParallelBailHookarg1, asyncParallelBailHookarg2, asyncParallelBailHookarg3, (function (_err2, _result2) { if (_err2) { if (_counter > 0) { if (2 < _results.length && ((_results.length = 3), (_results[2] = { error: _err2 }), _checkDone())) { _counter = 0; } else { if (--_counter === 0) _done(); } } } else { if (_counter > 0) { if (2 < _results.length && (_result2 !== undefined && (_results.length = 3), (_results[2] = { result: _result2 }), _checkDone())) { _counter = 0; } else { if (--_counter === 0) _done(); } } } })); } if (_counter <= 0) break; if (3 >= _results.length) { if (--_counter === 0) _done(); } else { var _fn3 = _x[3]; _fn3(asyncParallelBailHookarg1, asyncParallelBailHookarg2, asyncParallelBailHookarg3, (function (_err3, _result3) { if (_err3) { if (_counter > 0) { if (3 < _results.length && ((_results.length = 4), (_results[3] = { error: _err3 }), _checkDone())) { _counter = 0; } else { if (--_counter === 0) _done(); } } } else { if (_counter > 0) { if (3 < _results.length && (_result3 !== undefined && (_results.length = 4), (_results[3] = { result: _result3 }), _checkDone())) { _counter = 0; } else { if (--_counter === 0) _done(); } } } })); } } while (false);
从上面最终生成的BodyCode来看, 按照taps调用对应的监听函数,当任何一个监听函数先抛出错误,则会导致后面的监听函数不会执行,且会触发callAsync传入的callback,并将错误参数当成第一个参数传入;当有监听函数resolve了对应的值出去,最终callback内得到的值也是,按照taps顺序来决定返回优先级的
asyncParallelBailHook tapPromise 与 tapAsync 当有错误抛出时,二者的表现是有区别的
到这里所有的钩子函数都分析完了,我们可以看出,同步及异步串行钩子最好理解,异步并行钩子不怎么好理解;不过我们都可以通过实际的例子去把new Function的BodyCode部分打印出来,然后看钩子的效果
tapable的设计真的很精美,没有使用递归来控制执行顺序,而是通过new Function的方式来拼接代码执行顺序,需要悟悟其中的设计思想
The text was updated successfully, but these errors were encountered:
No branches or pull requests
Tabpable 类似node eventemitter的库 控制钩子的函数的发布与订阅,控制webpack的插件系统
Tapable暴露的hook
SyncHook 同步钩子
SyncBailHook 同步熔断钩子
SyncWaterfallHook 同步流水钩子
SyncLoopHook 同步循环钩子
AsyncParalleHook 异步并发钩子
AsyncParalleBailHook 异步并发熔断钩子
AsyncSeriesHook 异步串行钩子
AsyncSeriesBailHook 异步串行熔断钩子
AysncSeriesWaterfallHook 异步串行流水钩子
Hook分类
从功能区分
从类型区分
先看个基本例子
然后在跟着例子在使用的时候,比较难理解的一点是异步并行钩子的执行,自己想要了解具体是怎样实现的,所以直接看了下tapable的源码,具体去了解怎么注册监听及执行监听的、具体的钩子是怎么实现的
Tapable 2.2.0 版本
我们先已最简单的hook SyncHook来看
我们用一个例子与源码来看下,最终输出的
syncHook
从上面最终生成的BodyCode来看,按照taps顺序执行,其中一个出错,后续会中断执行,最终call的返回值为undefined
syncBailHook
从上面最终生成的BodyCode来看,按照taps顺序执行,其中一个出错,后续会中断执行,当其中一个监听函数有返回值(返回值不能是undefined 或者 undefined字符串),则后续的监听函数不会在执行,call的返回值为return的值
syncWaterfallHook
从上面最终生成的BodyCode来看,按照taps顺序执行,其中一个出错,后续会中断执行,前一个监听函数返回的值不为undefined则会被当成第一个参数传入到下一个监听函数,call最后的返回值是最后一个监听函数的return的值
syncLoopHook
从上面最终生成的BodyCode来看,按照taps顺序执行,其中一个出错,后续会中断执行,按顺序执行的过程中,监听函数没有返回undefined,则会按照顺序重新执行,直到所有的监听函数都返回undefined,call最终的返回值为undefiend
asyncSeriesHook tapPromise promise
从上面最终生成的BodyCode来看,没有最终返回值,按照顺序执行,只要其中一个抛出错误,中断后续执行,并进入catch捕获函数
asyncSeriesHook tapAsync callAsync
从上面最终生成的BodyCode来看, 没有最终返回值,按照taps顺序执行,只要其中一个抛出错误,中断后续执行,并执行callAsync传入的callback函数,第一个参数为接受到的错误,与promise区别的是,每个tapAsync传入的最后一个参数是一个next函数,改函数接受一个错误参数,如果传入该参数则直接调用callAsync的回调callback
对于asyncSeriesHook钩子 tapAsync、tapPromise效果一致
asyncSeriesBailHook tapPromise promise
从上面最终生成的BodyCode来看, 按照taps顺序执行,如果监听函数有返回值,则直接resolve,后续的监听函数不在执行,如果抛出错误与asyncSeriesHook钩子一样,通过catch函数捕获
asyncSeriesBailHook tapAsync callAsync
从上面最终生成的BodyCode来看, 按照taps顺序执行,如果监听函数有返回值,则直接调用callAsync传入的callback,并将返回的结果传入第二个参数,后续的监听函数不在执行,如果抛出错误则直接调用callAsync传入的callback并将错误信息传入第一个参数
asyncSeriesBailHook tapPromise 与 tapAsync 效果一致
asyncSeriesWaterfallHook tapPromise promise
从上面最终生成的BodyCode来看, 按照taps顺序执行,如果监听函数有返回值,则直接resolve,并将resolve的值替换第一个参数并传入到下一个监听函数,最终resolve出去的值为,最后一个监听函数resolve的值,如果抛出错误则中断执行,通过catch函数捕获
asyncSeriesWaterfallHook tapAsync callAsync
从上面最终生成的BodyCode来看, 按照taps顺序执行,如果监听函数有返回值,则调用next,并return的值替换第一个参数并传入到下一个监听函数,最终return出去的值为,最后一个监听函数return的值,如果抛出错误则中断执行,并在callAsync的callback内接受到第一个参数,值为错误信息
asyncSeriesWaterfallHook tapPromise 与 tapAsync的效果一致
asyncParallelHook tapPromise promise
从上面最终生成的BodyCode来看, 按照taps生成对应的promise,每个promise都会执行,当任何一个promise先抛出错误,则会导致最终执行的promise()进入catch
asyncParallel tapAsync callAsync
从上面最终生成的BodyCode来看, 按照taps调用对应的监听函数,当任何一个监听函数先抛出错误,则会导致后面的监听函数不会执行,且会触发callAsync传入的callback,并将错误参数当成第一个参数传入;注意这里与tapPromise注册方式有区别,区别在tapPromise注册的监听函数就算报错其它的监听函数都会执行,而tapAsync方式注册当其中有一个监听函数抛出错误,后续的就不执行了,原因在于tapPromise是在then方法内将_counter=0,而then是microtask,所以后面的promise会继续执行
asyncParallelHook tapPromise 与 tapAsync 当有错误抛出时,二者的表现是有区别的
asyncParallelBailHook tapPromise promise
从上面最终生成的BodyCode来看, 按照taps生成对应的promise,然后陆续执行promise,当其中一个promise先返回值,还得确认该监听函数在taps中的顺序,如果顺序也满足,则会进入promise()的then回调,并将返回值返回出去,如果不满足顺序,则会等待前面的监听函数resolve,如果有一个promise抛出错误且还未有其它promise先成功,则会进入promise()的catch回调
asyncParallelBailHook tapAsync callAsync
从上面最终生成的BodyCode来看, 按照taps调用对应的监听函数,当任何一个监听函数先抛出错误,则会导致后面的监听函数不会执行,且会触发callAsync传入的callback,并将错误参数当成第一个参数传入;当有监听函数resolve了对应的值出去,最终callback内得到的值也是,按照taps顺序来决定返回优先级的
asyncParallelBailHook tapPromise 与 tapAsync 当有错误抛出时,二者的表现是有区别的
到这里所有的钩子函数都分析完了,我们可以看出,同步及异步串行钩子最好理解,异步并行钩子不怎么好理解;不过我们都可以通过实际的例子去把new Function的BodyCode部分打印出来,然后看钩子的效果
总结
tapable的设计真的很精美,没有使用递归来控制执行顺序,而是通过new Function的方式来拼接代码执行顺序,需要悟悟其中的设计思想
The text was updated successfully, but these errors were encountered: