# tapable原理学习

# SyncHook 同步钩子

  • 按照添加函数添加的顺序执行
class SyncHook {
  constructor(){
    this.tasks = []
  }
  tap(task){
    this.tasks.push(task)
  }
  call(...args){
    this.tasks.forEach((task)=>{
      task(...args)
    })
  }
}

let syncHook = new SyncHook()
// tap 订阅
syncHook.tap(function(task){
  console.log(task,1)
})
syncHook.tap(function(task){
  console.log(task,2)
})

// call 发布
syncHook.call('process')
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

# SyncBailHook 同步保险钩子

  • 为了保险,在每个函数钩子执行之后,判断是否返回非undefined得值,如果返回则不继续往下执行
class SyncBailHook{
  constructor(){
    this.tasks = []
  }
  tap(name,task){
    this.tasks.push(task)
  }
  call(...args){
    let result ,index=0;
    do{
      result=this.tasks[index++](...args)
    }while(result === undefined && index < this.tasks.length)
  }
}


let syncBailHook = new SyncBailHook()
// tap 订阅
syncBailHook.tap(function(task){
  console.log(task,1)
  return false
})
syncBailHook.tap(function(task){
  console.log(task,2)
})

// call 发布
syncBailHook.call('process')
// 只输出 process 1,因为第一个注册的函数返回了非undifined的值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

# SyncWaterfallHook 同步瀑布流钩子

  • 将前一个任务输出的结果传递给下一个任务,像瀑布一样向下流转
  • 这里运用了 Arrayreduce 来处理数据向下流转
class SyncWaterfallHook {
  constructor(name){
    this.tasks = []
  }
  tap(name,task){
    this.tasks.push(task)
  }
  call(...args){
    let [first,...others] = this.tasks
    others.reduce((value,task)=>{
      return task(value)
    },first(...args))
  }
}
let SyncWaterfallHook = new SyncWaterfallHook()
SyncWaterfallHook.tap('学拼音', function (data) {
  console.log(data);
  return '学会拼音'
})
SyncWaterfallHook.tap('说话', function (data) {
  console.log(data);
  return '学会说话'
})
SyncWaterfallHook.tap('写字', function (data) {
  console.log(data);
  return '学会写字'
})

hook.call('small husky')
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

# SyncLoopHook 同步循环钩子

  • 每个注册的任务执行后,只要返回值不为undefined就会一直循环执行当前任务

TIP

function函数不写return,默认返回undefined

class SyncLoopHook {
  constructor(name){
    this.tasks = []
  }
  tap(name,task){
    this.tasks.push(task)
  }
  call(...args){
    this.tasks.forEach(task=>{
      let result = null
      do {
        result = task(...args)
      } while (result!==undefined);
    })
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# AsyncParalleHook 异步并行钩子

# (1) use callback

  • 当所有任务并行完成后才执行最后的回调方法
  • 每个任务完成之后,都会调用回调,在回调函数中进行判断是否全部的任务执行完成
  • 主要实现方式,在callAsync中定义一个变量currentTaskTotal,用来保存有多少个任务执行完成了,每次任务执行完成都会调用回调函数done来对currentTaskTotal++,并且判断currentTaskTotal是否和总任务数量相等,相等则代表全部的任务执行完成,执行最后的callAsync的回调
class AsyncParallelHook {
  constructor(){
    this.tasks = []
  }
  tapAsync(name,task){
    this.tasks.push(task)
  }
  callAsync(...args){
    let callBack = args.pop()
    let index = 0
    let done =()=>{
      index++
      if(index === this.tasks.length){
        callBack()
      }
    }
    this.tasks.forEach(task=>{
      task(...args,done)
    })
  }
}

//  执行
let AsyncParallelHook = new AsyncParallelHook()
AsyncParallelHook.tapAsync('1', function (data,cb) {
  setTimeout(()=>{
    console.log(1);
    cb()
  },1000)
})
AsyncParallelHook.tapAsync('2', function (data,cb) {
  setTimeout(()=>{
    console.log(2);
    cb()
  },2000)
})
AsyncParallelHook.callAsync('end',()=>{
  console.log('over');
})

// 结果
1
2
over
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44

# (2) use promise

  • 注册的任务都返回promise
  • 通过PromiseAll方法来处理来给出任务终止
class AsyncParallelHookPromise {
  constructor() {
    this.tasks = []
  }
  tapPromise(name, task) {
    this.tasks.push(task)
  }
  promise(...args) {
    let promiseTask = this.tasks.map(task => task(...args))
    return Promise.all(promiseTask)
  }
}

//  执行
let asyncParallelHookPromise = new AsyncParallelHookPromise()
asyncParallelHookPromise.tapPromise('1', function (data) {
  return new Promise((resolve,reject)=>{
    setTimeout(() => {
      console.log(1, data);
      resolve()
    }, 1000)
  })
})
asyncParallelHookPromise.tapPromise('2', function (data) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log(2, data);
      resolve()
    }, 1000)
  })
})
asyncParallelHookPromise.promise('end').then(() => {
  console.log('over');
})

// 结果
1 end
2 end
over
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39

# AsyncSeriersHool 异步串行钩子

# (1) use callbask

  • 执行完一个任务之后才能执行下一个任务,所有任务完成,调最终回调
  • 使用 next函数 index 变量来实现
class AsyncSeriesHook {
  constructor() {
    this.tasks = []
  }
  tapAsync(name, task) {
    this.tasks.push(task)
  }
  callAsync(...args) {
    let index = 0
    let finalCallBack = args.pop()
    let next = ()=>{
      if (index === this.tasks.length) return finalCallBack()
      let currentTask = this.tasks[index++]
      currentTask(...args, next)
    }
    next()
  }
}

//  执行
let asyncSeriesHook = new AsyncSeriesHook()
asyncSeriesHook.tapAsync('1', function (data, cb) {
  setTimeout(() => {
    console.log(1, data);
    cb()
  }, 1000)
})
asyncSeriesHook.tapAsync('2', function (data, cb) {
  setTimeout(() => {
    console.log(2, data);
    cb()
  }, 1000)
})
asyncSeriesHook.callAsync('end',() => {
  console.log('over');
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

# (2) use promise

  • 借助Arrayreduce来将上一个执行的函数的返回值向下传递
  • lastPromise :我当前要执行的promisenextTask:为下一个任务
  • 最终返回
class AsyncSeriesPromiseHook {
  constructor(name) {
    this.tasks = []
  }
  tapPromise(name, task) {
    this.tasks.push(task)
  }
  promise(...args) {
    let [first, ...others] = this.tasks
    return others.reduce((lastPromise, nextTask) => {
      return lastPromise.then(() => nextTask(...args))
    }, first(...args))
  }
}

//  执行
let asyncSeriesPromiseHook = new AsyncSeriesPromiseHook()
asyncSeriesPromiseHook.tapPromise('1', function (data) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log(1, data);
      resolve()
    }, 1000)
  })
})
asyncSeriesPromiseHook.tapPromise('2', function (data) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log(2, data);
      resolve()
    }, 1000)
  })
})
asyncSeriesPromiseHook.promise('end').then(() => {
  console.log('over');
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

# AsyncSeriesWaterfallHook 异步串行瀑布流钩子

// 异步串行钩子
class AsyncSeriesWaterfallHook {
  constructor() {
    this.tasks = []
  }
  tapAsync(name, task) {
    this.tasks.push(task)
  }
  callAsync(...args) {
    let index = 0
    let finalCallBack = args.pop()
    let next = (err, data) => {
      let currentTask = this.tasks[index]
      if (!currentTask) return finalCallBack()
      if(index === 0){
        currentTask(...args, next)
      }else{
        currentTask(data, next)
      }
      index++
    }
    next()
  }
}

//  执行
let asyncSeriesWaterfallHook = new AsyncSeriesWaterfallHook()
asyncSeriesWaterfallHook.tapAsync('1', function (data, cb) {
  setTimeout(() => {
    console.log(1, data);
    cb(null, '结果')
  }, 1000)
})
asyncSeriesWaterfallHook.tapAsync('2', function (data, cb) {
  setTimeout(() => {
    console.log(2, data);
    cb(null, '结果')
  }, 1000)
})
asyncSeriesWaterfallHook.callAsync('end', () => {
  console.log('over');
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
Last Updated: 1/23/2022, 10:16:22 AM