关于javascript:源码webpack02前置知识-Tapable

导航

[[深刻01] 执行上下文](https://juejin.im/post/684490…
[[深刻02] 原型链](https://juejin.im/post/684490…
[[深刻03] 继承](https://juejin.im/post/684490…
[[深刻04] 事件循环](https://juejin.im/post/684490…
[[深刻05] 柯里化 偏函数 函数记忆](https://juejin.im/post/684490…
[[深刻06] 隐式转换 和 运算符](https://juejin.im/post/684490…
[[深刻07] 浏览器缓存机制(http缓存机制)](https://juejin.im/post/684490…
[[深刻08] 前端平安](https://juejin.im/post/684490…
[[深刻09] 深浅拷贝](https://juejin.im/post/684490…
[[深刻10] Debounce Throttle](https://juejin.im/post/684490…
[[深刻11] 前端路由](https://juejin.im/post/684490…
[[深刻12] 前端模块化](https://juejin.im/post/684490…
[[深刻13] 观察者模式 公布订阅模式 双向数据绑定](https://juejin.im/post/684490…
[[深刻14] canvas](https://juejin.im/post/684490…
[[深刻15] webSocket](https://juejin.im/post/684490…
[[深刻16] webpack](https://juejin.im/post/684490…
[[深刻17] http 和 https](https://juejin.im/post/684490…
[[深刻18] CSS-interview](https://juejin.im/post/684490…
[[深刻19] 手写Promise](https://juejin.im/post/684490…
[[深刻20] 手写函数](https://juejin.im/post/684490…

[[react] Hooks](https://juejin.im/post/684490…

[[部署01] Nginx](https://juejin.im/post/684490…
[[部署02] Docker 部署vue我的项目](https://juejin.im/post/684490…
[[部署03] gitlab-CI](https://juejin.im/post/684490…

[[源码-webpack01-前置常识] AST形象语法树](https://juejin.im/post/684490…
[[源码-webpack02-前置常识] Tapable](https://juejin.im/post/684490…
[[源码-webpack03] 手写webpack – compiler简略编译流程](https://juejin.im/post/684490…
[[源码] Redux React-Redux01](https://juejin.im/post/684490…
[[源码] axios ](https://juejin.im/post/684490…
[[源码] vuex ](https://juejin.im/post/684490…
[[源码-vue01] data响应式 和 初始化渲染 ](https://juejin.im/post/684490…
[[源码-vue02] computed 响应式 – 初始化,拜访,更新过程 ](https://juejin.im/post/684490…
[[源码-vue03] watch 侦听属性 – 初始化和更新 ](https://juejin.im/post/684490…
[[源码-vue04] Vue.set 和 vm.$set ](https://juejin.im/post/684490…
[[源码-vue05] Vue.extend ](https://juejin.im/post/684490…

[[源码-vue06] Vue.nextTick 和 vm.$nextTick ](https://juejin.im/post/684790…

前置常识

一些单词

bail:保险,保障
parallel:并行的
series:串行的,间断的

optional:可选的
// All Hook constructors take one optional argument, which is a list of argument names as strings.
// 所有的 Hook 构造函数都接管一个可选的参数,是一个参数名称字符串组成的数组

practice:实际
// The best practice is to expose all hooks of a class in a hooks property
// 最佳实际是在hooks属性中公开类的所有钩子

accelerate:减速
brake:刹车
waterfall:瀑布

公布订阅模式

  • 发布者:publisher
  • 订阅者:subscribe
  • 中介:topic/event channel
  • ( <font color=red>中介</font> ) 既要接管 ( <font color=red>发布者</font> ) 公布的音讯,又要将音讯派发给订阅了该事件的 ( <font color=red>订阅者</font> )

    • ( 中介 ) 须要依据 ( 不同的事件 ),贮存相应的 ( 订阅者 ) 信息
    • 通过 ( 中介对象 ) 齐全 ( 解耦 ) 了 ( 发布者 ) 和 ( 订阅者 )
  • 公布订阅模式代码实现 – es5

    // 公布订阅模式
    
    <script>
    const pubsub = {};
    // pubsub中介对象
    // pubsub中具备subscribe,unSubscribe,publish等办法
    
    (function(pubsub) {
      const topics = {};
      // topics是一个 ( 事件 ) 和订阅该事件的 ( 订阅者 ) 对应关系的一个对象
      // key:是不同的事件
      // value:是订阅了该事件的订阅者的数组
      // event_name: [{fname: fn.name, fn}]
      
      // 订阅
      pubsub.subscribe = function(eventName, fn) {
        // 不存在该事件对应的订阅者对象数组, 新建
        // 存在,向数组中增加订阅者对象
        if (!topics[eventName]) {
          topics[eventName] = []
        }
        topics[eventName].push({
          fname: fn.name,
          fn
        })
      }
    
      // 公布
      pubsub.publish = function(eventName, params) {
        if (!topics[eventName].length) {
          return
        }
        // 取出所有订阅者中的更新函数,并执行
        topics[eventName].forEach((item, index) => {
          item.fn(params)
        })
      }
    
      // 勾销订阅
      pubsub.unSubscribe = function(eventName, fname) {
        if (!topics[eventName]) {
          return
        }
        topics[eventName].forEach((item, index) => {
          if (item.fname === fname) {
            topics[eventName].splice(index, 1) // 删除该事件对应的订阅者对象的更新函数同名的订阅者对象
          }
        })
      }
    })(pubsub)
    
    function goFn1(params) {
      console.log('goFn1', params)
    }
    function goFn2(params) {
      console.log('goFn2', params)
    }
    
    pubsub.subscribe('go', goFn1) // 订阅
    pubsub.subscribe('go', goFn2)
    
    pubsub.publish('go', '1111111') // 公布
    
    pubsub.unSubscribe('go', goFn2.name) // 勾销订阅
    pubsub.publish('go', '22222')
    </script>
  • 公布订阅模式代码实现 – es6

    <script>
    class PubSub { // PubSub类
      constructor() {
        this.topics = {} 
        // 保护事件和订阅者对应关系的对象
        // 事件 <-> 订阅者数组
      }
      subscribe(eventName, fn) { // 订阅
        if (!this.topics[eventName]) {
          this.topics[eventName] = []
        }
        this.topics[eventName].push({
          fname: fn.name,
          fn
        })
      }
      publish(eventName, params) { // 公布
        if (!this.topics[eventName].length) {
          return
        }
        this.topics[eventName].forEach((item, index) => {
          item.fn(params)
        })
      }
      unSubscribe(eventName, fname) { // 勾销订阅
        if (!this.topics[eventName]) {
          return
        }
        this.topics[eventName].forEach((item, index) => {
          if (item.fname === fname) {
            this.topics[eventName].splice(index, 1)
          }
        })
      }
    }
    
    const pubsub = new PubSub()
    
    function goFn1(params) {
      console.log('goFn1', params)
    }
    function goFn2(params) {
      console.log('goFn2', params)
    }
    
    pubsub.subscribe('go', goFn1)
    pubsub.subscribe('go', goFn2)
    
    pubsub.publish('go', '1') // 公布
    
    pubsub.unSubscribe('go', goFn2.name) // 勾销订阅
    pubsub.publish('go', '2')
    </script>

Tapable

  • 外围就是公布订阅模式
  • 装置:npm install tapable -S

SyncHook

  • 同步形式的公布订阅模式
  • const synchook = new SyncHook(['params'])
  • synchook.tap() // 订阅,注册
  • synchook.call() // 公布,执行

    const { SyncHook } = require('tapable')
    
    class Work {
    constructor() {
      this.hooks = {
        city: new SyncHook(['who']) // 这里要求在订阅事件时的回调中须要传参
      }
    }
    // 订阅,注册
    tap(eventName) {
      this.hooks.city.tap(eventName, function(who){
        console.log(who, eventName)
      })
    }
    // 公布,执行
    publish() {
      this.hooks.city.call('woow_wu7')
    }
    }
    const work = new Work()
    work.tap('chongqing') // 订阅
    work.tap('hangzhou')
    work.publish() // 公布
    
    --------- 比照参考
    const { SyncHook } = require('tapable')
    
    class Lesson {
    constructor() {
      this.hook = {
        arch: new SyncHook(['name']) // 同步钩子,在 .tap()函数的参数函数中接管一个参数
      }
    }
    tap() {
      this.hook.arch.tap('react', (name) => { // name参数是在 .call()时传入的
        console.log('react', name)
      })
      this.hook.arch.tap('vue', (name) => {
        console.log('vue', name)
      })
    }
    publish() {
      this.hook.arch.call('woow_wu7')
    }
    }
    
    const lesson = new Lesson(['name'])
    lesson.tap() // 注册
    lesson.publish() // 公布

SyncHook模仿实现

class SyncHook {
  constructor() {
    this.observers = []
    // observers 是观察者对象组成的数组,观察者对象中蕴含event事件名称, 和fn工作函数
    // [{event:eventName, fn: fn}]
  }

  // 注册观察者对象
  // 更简略点能够间接注册事件
  tap(eventName, fn) {
    this.observers.push({
      event: eventName,
      fn
    })
  }

  // 执行注册的观察者对象中的fn函数
  call(...params) {
    this.observers.forEach(item => item.fn(item.event, ...params))
  }
}

const synchook = new SyncHook(['params'])

synchook.tap('react', function(eventName, name) {
  console.log(eventName, name)
})
synchook.tap('vue', function(eventName, name) {
  console.log(eventName, name)
})

synchook.call('woow_wu7')

SyncBailHook

  • SyncBailHook提供了 ( 终止执行 ) 订阅者数组中监听函数的机制
  • 当 .tap() 中的监听函数返回值是 ( <font color=red>!undefined</font> ) 时会 ( <font color=red>终止执行</font> ) .call() 函数

    let { SyncBailHook } = require('tapable');
    
    class Lesson {
    constructor() {
      this.hooks = {
        arch: new SyncBailHook(['name'])
      };
    }
    
    tap() {
      this.hooks.arch.tap('vue', function(name) {
        console.log('vue', name);
        return 'SyncBailHook当返回值是!undefined时,就会进行执行'; 
        // ---------------------------- SyncBailHook当.tap()的参数监听函数返回值是 ( !undefined  ) 时就不再往下继续执行
        // return undefined  ---------- 返回值是undefined则不受影响,因为函数默认的返回值就是undefined
      });
    
      this.hooks.arch.tap('react', function(name) {
        console.log('react', name);
      });
    }
    
    start() {
      this.hooks.arch.call('woow_wu7');
    }
    }
    
    let lesson = new Lesson();
    lesson.tap();
    lesson.start();
    

SyncBailHook模仿实现

class SyncBailHook {
  constructor() {
    this.observers = []
    // observers 是观察者对象组成的数组,观察者对象中蕴含event事件名称, 和fn工作函数
    // [{event:eventName, fn: fn}]
  }

  // 注册观察者对象
  // 更简略点能够间接注册事件
  tap(eventName, fn) {
    this.observers.push({
      event: eventName,
      fn
    })
  }

  // 执行注册的观察者对象中的fn函数
  call(...params) {
    // this.observers.forEach(item => item.fn(item.event, ...params))
    let res = undefined;
    for(let i = 0; i < this.observers.length; i++) {
      const currentObj = this.observers[i] // ---------------------------------- 屡次用到,就缓存晋升性能
      res = currentObj.fn(currentObj.event, ...params)
      if (res !== undefined) { 
        // --------------------------- 循环数组时做判断,如果函数返回值是 (!undefined) 就跳出 .call() 函数
        return
      }
    }
  }
}

const syncBailHook = new SyncBailHook(['params'])

syncBailHook.tap('react', function(eventName, name) {
  console.log(eventName, name)
  return 'stop'
})
syncBailHook.tap('vue', function(eventName, name) {
  console.log(eventName, name)
})

syncBailHook.call('woow_wu7')

SyncWaterfallHook

let { SyncWaterfallHook } = require('tapable');
// SyncWaterfallHook将上一个.tap() 的参数回调函数的 ( 返回值 ) 作为 ( 参数 ) 传给下一个 .tap() 的参数回调函数

class Lesson {
  constructor() {
    this.hooks = {
      arch: new SyncWaterfallHook(['name'])
    };
  }
  // 注册监听函数
  tap() {
    
    this.hooks.arch.tap('vue', function(name) {
      console.log('vue', name);
      return 'vue不错'; 
      // ---------------------------- SyncBailHook当返回值是 !undefined 时就不再往下继续执行
      // return undefined  ---------- 返回值是undefined则不受影响,因为函数默认的返回值就是undefined
    });

    this.hooks.arch.tap('react', function(name) {
      console.log('react', name); // 这里的name就是上一个回调的返回值,即vue不错
      return 'react不错'
    });

    this.hooks.arch.tap('node', function(name) {
      console.log('node', name); // 这里的name是上一个回调的返回值,即react不错
    });
    
  }
  start() {
    this.hooks.arch.call('woow_wu7');
  }
}

let lesson = new Lesson();
lesson.tap();
lesson.start();

// vue woow_wu7
// react vue不错
// node react不错

SyncWaterfallHook模仿实现

  • 次要利用数组的 reduce() 办法迭代

    class SyncWaterfallHook {
    constructor() {
      this.observers = []
      // observers 是观察者对象组成的数组,观察者对象中蕴含event事件名称, 和fn工作函数
      // [{event:eventName, fn: fn}]
    }
    
    // 注册观察者对象
    // 更简略点能够间接注册事件
    tap(eventName, fn) {
      this.observers.push({
        event: eventName,
        fn
      })
    }
    
    // 执行注册的观察者对象中的fn函数
    call(...params) {
      // this.observers.forEach(item => item.fn(item.event, ...params))
      const [first, ...rest] = this.observers
      const fisrtRes = this.observers[0].fn(...params)
    
      rest.reduce((a, b) => {
        return b.fn(a)
      }, fisrtRes)
      // 第一次:当reduce存在第二个参数时,a = fisrtRes, b则就是数组的第一个成员
      // 第二次:a = 第一次的返回值b.fn(a),b则是数组的第二个成员
      // ...
    }
    }
    
    const syncWaterfallHook = new SyncWaterfallHook(['params'])
    
    syncWaterfallHook.tap('react', function(name) {
    console.log('react', name)
    return 'react ok'
    })
    syncWaterfallHook.tap('vue', function(name) {
    console.log('vue', name)
    return 'vue ok'
    })
    syncWaterfallHook.tap('node', function(name) {
    console.log('node', name)
    })
    
    syncWaterfallHook.call('woow_wu7')
    
    // react woow_wu7
    // vue react ok
    // node vue ok

SyncLoopHook

  • 当 以后监听函数返回 ( !undefined ) 时会屡次执行,直到以后的监听函数返回undefined时进行执行以后函数,继续执行下一个函数

    let { SyncLoopHook } = require('tapable');
    // SyncLoopHook 
    // 当 .tap()的回调函数中返回值是 !undefined 时就会屡次执行,晓得返回值是undefined就会终止执行,则继续执行下一个 .tap()
    
    class Lesson {
    constructor() {
      this.hooks = {
        arch: new SyncLoopHook(['name'])
      };
    }
    index = 0;
    // 这里index是挂在Lesson.prototype上的
    // 如果在constructor中申明 this.index = 0 则 this示意实例对象
    // 之所以在tap()中能够调用this.index是因为tap的调用切实less实例上调用的,所以this示意实例则能够获取到this.index
    
    // 注册监听函数
    tap() {
      const that = this;
      this.hooks.arch.tap('react', function(name) {
        console.log('react', name);
        return ++that.index === 3 ? undefined : 'react不错';
        // 返回值不为undefined就会始终执行该tap函数,直到为undefined
      });
      this.hooks.arch.tap('node', function(name) {
        console.log('node', name);
      });
    }
    start() {
      this.hooks.arch.call('woow_wu7');
    }
    }
    
    let lesson = new Lesson();
    lesson.tap();
    lesson.start();
    

SyncLoopHook模仿实现

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <script>
class SyncLoopHook {
  constructor() {
    this.observers = [];
    // observers 是观察者对象组成的数组,观察者对象中蕴含event事件名称, 和fn工作函数
    // [{event:eventName, fn: fn}]
  }

  // 注册观察者对象
  // 更简略点能够间接注册事件
  tap(eventName, fn) {
    this.observers.push({
      event: eventName,
      fn
    })
  }

  // 执行注册的观察者对象中的fn函数
  call(...params) {
    this.observers.forEach(item => {
      let res;
      do {
        res = item.fn(...params)
      } while(res !== undefined);
    })
  }
}

const syncLoopHook = new SyncLoopHook(['params'])

let count = 0;

syncLoopHook.tap('react', function(name) {
  console.log('react', name)
  return ++count === 3 ? undefined : 'go on'
})
// 留神:函数的作用域是函数定义时所在的对象
// 当在 .call() 办法中屡次调用时,count会减少,因为函数和变量的作用域都是在申明时确认的

syncLoopHook.tap('vue', function(name) {
  console.log('vue', name)
})
syncLoopHook.tap('node', function(name) {
  console.log('node', name)
})

syncLoopHook.call('woow_wu7')
  </script>
</body>
</html>

AsyncParallelHook – 通过 .tapAsync(_, (name, cb)=>{异步代码}).callAsync(_, ()=>{})调用

  • 异步并行的钩子
  • <font color=red>parallel:是并行的意思</font>
  • <font color=red>series:是串行的意思</font>

    let { AsyncParallelHook } = require('tapable');
    // AsyncParallelHook是异步并行的钩子
    // parallel:并行
    // series:串行
    
    class Lesson {
    constructor() {
      this.hooks = {
        arch: new AsyncParallelHook(['name'])
      };
    }
    
    // 注册监听函数
    tap() {
      this.hooks.arch.tapAsync('react', function (name, cb) { // ----------- tapAsync 异步注册
        setTimeout(function () {
          console.log('react', name);
          cb(); // --------------------------------------------------------- cb()示意执行结束
        }, 1000)
      });
      this.hooks.arch.tapAsync('node', function (name, cb) {
        setTimeout(function () {
          console.log('node', name);
          cb(); // -----------------只有有一个cb()没有执行,在callAsync()中的回调函数就不会触发
        }, 1000)
      });
    }
    start() {
      this.hooks.arch.callAsync('woow_wu7', function () { // --------------- callAsync异步执行
        console.log('end')
      });
    }
    }
    
    let lesson = new Lesson();
    lesson.tap();
    lesson.start();
    
    

AsyncParallelHook模仿实现

class AsyncParallelHook {
  constructor() {
    this.observers = [];
  }
  tapAsync(eventName, fn) {
    this.observers.push({
      event: eventName,
      fn
    })
  }
  callAsync(...params) {
    const callbackFn = params.pop() 
    // 取出callAsync()的最初一个参数,这里只有两个参数,即取出 cb 回调
    // 留神:pop(), shift() 返回被删除的元素,扭转原数组
    // 留神:push(), unshift() 返回操作后的数组长度,扭转原数组

    const that = this
    // 固定thiss

    let count = 0
    // 计数,用于统计 cb 回调执行的次数

    function done() {
      count++;
      if (count === that.observers.length) { 
        // 保障每一个 .tap() 中都执行了 cb() 回调
        // 相等阐明每个 .tap()中都执行了 cb()
        // 阐明:此条件就是执行 callAsync()中的参数回调函数的条件
        callbackFn()
      }
    }
    this.observers.forEach((item, index) => {
      item.fn(...params, done)
      // done函数作为参数传递给 .tapAsync()在外部被调用
    })
  }
}

const asyncParallelHook = new AsyncParallelHook(['params'])

asyncParallelHook.tapAsync('react', function(name, cb) {
  setTimeout(() => {
    console.log('react', name)
    cb()
  }, 1000);
})
asyncParallelHook.tapAsync('vue', function(name, cb) {
  setTimeout(() => {
    console.log('vue', name)
    cb()
  }, 1000);
})
asyncParallelHook.tapAsync('node', function(name, cb) {
  setTimeout(() => {
    console.log('node', name)
    cb()
  }, 1000);
})

asyncParallelHook.callAsync('woow_wu7', function() {
  console.log('end')
})

AsyncParallelHook – 通过 .tapPromise().promise().then()调用

const { AsyncParallelHook } = require('tapable')

class Lesson {
  constructor() {
    this.hook = {
      arch: new AsyncParallelHook(['name'])
    }
  }
  tap() {
    this.hook.arch.tapPromise('react', name => {
      return new Promise((resolove) => {
        setTimeout(() => {
          console.log('react', name)
          return resolove()
        }, 1000);
      })
    })
    this.hook.arch.tapPromise('vue', name => {
      return new Promise((resolove) => {
        setTimeout(() => {
          console.log('react', name)
          return resolove()
        }, 1000);
      })
    })
  }
  publish() {
    this.hook.arch.promise('woow_wu7').then(() => {
      console.log('end')
    })
  }
}

const lesson = new Lesson(['name'])
lesson.tap() // 注册
lesson.publish() // 公布

AsyncParallelHook – 通过 .tapPromise().promise().then()调用 – 模仿实现

class AsyncParallelHook {
  constructor() {
    this.observers = [];
  }
  tapPromise(eventName, fn) {
    this.observers.push({
      event: eventName,
      fn
    })
  }
  promise(...params) {
    const promiseArr = this.observers.map(item => item.fn(...params))
    return Promise.all(promiseArr) 
    // 返回一个Promise.all()函数
    // 所有 resolve() 才 resolve()
    // 一个 rejectI() 就 reject()
  }
}

const asyncParallelHook = new AsyncParallelHook(['params'])

asyncParallelHook.tapPromise('react', function (name) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log('react', name)
      return resolve()
    }, 1000);
  })
})
asyncParallelHook.tapPromise('vue', function (name) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log('react', name)
      return resolve()
    }, 1000);
  })
})
asyncParallelHook.promise('node').then(function (name) {
  console.log('end')
})

AsyncSeriesHook – 异步串行钩子

  • 在前一个.tap()执行完才会继续执行下一个.tap(),所有的.tap()中的cb()都执行完,才执行.callAsync()中的回调函数

    const { AsyncSeriesHook } = require('tapable')
    
    class Lesson {
    constructor() {
      this.hook = {
        arch: new AsyncSeriesHook(['name'])
      }
    }
    tap() {
      this.hook.arch.tapAsync('react', (name, cb) => {
        setTimeout(() => {
          console.log('react', name)
          cb()
        }, 1000);
      })
      this.hook.arch.tapAsync('vue', (name, cb) => {
        setTimeout(() => {
          console.log('vue', name)
          cb()
        }, 1000);
      })
    }
    publish() {
      this.hook.arch.callAsync('woow_wu7', () => {
        console.log('end')
      })
    }
    }
    
    const lesson = new Lesson(['name'])
    
    lesson.tap()
    lesson.publish()

AsyncSeriesHook模仿实现

class AsyncSeriesHook {
  constructor() {
    this.observers = []
  }
  tapAsync(name, fn) {
    this.observers.push({
      event_name: name,
      fn
    })
  }
  callAsync(...params) {
    const lastCallback = params.pop()
    let index = 0;
    const that = this;
    function next() {
      if (index === that.observers.length) {
        return lastCallback()
        // 如果不等,就永远不会执行 lastCallback()
      }
      that.observers[index++].fn(...params, next)
      // index++ 
      // 先整个赋值即 that.observes[0]
      // 再 index = index + 1 => index = 1

      // 递归next()办法,直到 index === that.observers.length 后则返回 lastCallbackk() 进行递归
    }
    next()
  }
}

const asyncSeriesHook = new AsyncSeriesHook(['name'])

asyncSeriesHook.tapAsync('react', function(name, cb) {
  setTimeout(() => {
    console.log('react', name)
    cb()
  }, 1000);
})
asyncSeriesHook.tapAsync('vue', function(name, cb) {
  setTimeout(() => {
    console.log('vue', name)
    cb()
  }, 1000);
})
asyncSeriesHook.callAsync('woow_wu7', function() {
  console.log('end')
})

AsyncSeriesWaterfullHook

const { AsyncSeriesWaterfallHook } = require('tapable')

class Lesson {
  constructor() {
    this.hook = {
      arch: new AsyncSeriesWaterfallHook(['name'])
    }
  }
  tap() {
    this.hook.arch.tapAsync('react', (name, cb) => {
      setTimeout(() => {
        console.log('react', name)
        cb(null, 'next-react')
        // 当第一个参数布尔值是false时, 将传递 'next-react' 作为下一个 .tapAsynce() 中参数回调函数的参数
        // 当第一个参数布尔值是true时,将中断下一个 .tapAsynce() 的执行
           // 留神: 尽管会中断下一个.tapAsynce()
           // 然而因为调用了cb()且为.tapAsynce()的总个数时,所以callAsync的第二个参数回调会执行,即会打印'end'
      }, 1000);
    })
    this.hook.arch.tapAsync('vue', (name, cb) => {
      setTimeout(() => {
        console.log('vue', name)
        cb(null, 'next-vue')
        // 如果是cb('null', 'next-vue') 第一个参数布尔值是true,则不会执行下一个.tapAsynce()
      }, 1000);
    })
  }
  publish() {
    this.hook.arch.callAsync('woow_wu7', () => {
      console.log('end')
    })
  }
}

const lesson = new Lesson(['name'])

lesson.tap()
lesson.publish()

AsyncSeriesWaterfullHook模仿实现

class AsyncSeriesWaterfallHook {
  constructor() {
    this.observers = []
  }
  tapAsync(name, fn) {
    this.observers.push({
      event_name: name,
      fn
    })
  }
  callAsync(...params) {
    const lastCallback = params.pop()
    let index = 0;
    const that = this;
    function next(err, data) {
      let task = that.observers[index]
      if (!task || err || index === that.observers.length) {
        // 如果该task不存在
        // 或者 err存在
        // 或者 index达到最大长度,其实能够不判断,因为index超出that.observers.length时,task将不存在了,满足第一个条件
        // 满足以上条件,都间接返回lastCallback
        return lastCallback()
      }
      if (index === 0) {
        task.fn(...params, next)
      } else {
        task.fn(data, next)
      }
      index++
    }
    next()
  }
}

const asyncSeriesWaterfallHook = new AsyncSeriesWaterfallHook(['name'])

asyncSeriesWaterfallHook.tapAsync('react', function(name, cb) {
  setTimeout(() => {
    console.log('react', name)
    cb(null, 'next-react')
  }, 1000);
})
asyncSeriesWaterfallHook.tapAsync('vue', function(name, cb) {
  setTimeout(() => {
    console.log('vue', name)
    cb(null, 'next-vue')
  }, 1000);
})
asyncSeriesWaterfallHook.callAsync('woow_wu7', function() {
  console.log('end')
})

材料

tapable https://github.com/webpack/ta…
tapable https://juejin.im/post/684490…
tapable https://juejin.im/post/684490…

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理