模拟手写AsyncParallelHook源码部分。
let Hook = require('./Hook.js') class HookCodeFactory { args({ after, before } = {}) { let allArgs = this.options.args if (before) allArgs = [before].concat(allArgs) if (after) allArgs = allArgs.concat(after) return allArgs.join(',') // ["name", "age"]===> name, age } head() { return `"use strict";var _context;var _x = this._x;` } content() { let code = `var _counter = ${this.options.taps.length};var _done = (function () { _callback(); });` for (var i = 0; i < this.options.taps.length; i++) { code += `var _fn${i} = _x[${i}];_fn${i}(name, age, (function () { if (--_counter === 0) _done(); }));` } return code } setup(instance, options) { // 先准备后续需要使用到的数据 this.options = options // 这里的操作在源码中是通过 init 方法实现,而我们当前是直接挂在了 this 身上 instance._x = options.taps.map(o => o.fn) // this._x = [f1, f2, ....] } create() { // 核心就是创建一段可执行的代码体然后返回 let fn // fn = new Function("name, age", "var _x = this._x, var _fn0 = _x[0]; _fn0(name, age);") fn = new Function( this.args({ after: '_callback' }), this.head() + this.content() ) return fn } } let factory = new HookCodeFactory() class AsyncParallelHook extends Hook { constructor(args) { super(args) } compile(options) { // {taps: [{}, {}], args: [name, age]} factory.setup(this, options) return factory.create(options) } } module.exports = AsyncParallelHook
class Hook { constructor(args = []) { this.args = args this.taps = [] // 将来用于存放组装好的 {} this._x = undefined // 将来在代码工厂函数中会给 _x = [f1, f2, f3....] } tap(options, fn) { if (typeof options === 'string') { options = { name: options } } options = Object.assign({ fn }, options) // { fn:... name:fn1 } // 调用以下方法将组装好的 options 添加至 [] this._insert(options) } tapAsync(options, fn) { if (typeof options === 'string') { options = { name: options } } options = Object.assign({ fn }, options) // { fn:... name:fn1 } // 调用以下方法将组装好的 options 添加至 [] this._insert(options) } _insert(options) { this.taps[this.taps.length] = options } call(...args) { // 01 创建将来要具体执行的函数代码结构 let callFn = this._createCall() // 02 调用上述的函数(args传入进去) return callFn.apply(this, args) } callAsync(...args) { let callFn = this._createCall() return callFn.apply(this, args) } _createCall() { return this.compile({ taps: this.taps, args: this.args }) } } module.exports = Hook
const SyncHook=require('./AsyncParallelHook.js') //const { AsyncParallelHook } =require('tapable') let hook=new AsyncParallelHook(["name","age"]) hook.tapAsync('fn1',function(name,age,callback){ console.log('fn1--->',name,age) callback() }) hook.tapAsync('fn2',function(name,age,callback){ console.log('fn2--->',name,age) callback() }) hook.callAsync('lw',18,function(){ console.log('end...') })