# tapable

# SyncHook

同步钩子。依次执行注册的回调。

class SyncHook {
    constructor() {
        this.tasks = []
    }
    tap(name, callback) {
        this.tasks.push(callback)
    }
    call() {
        this.tasks.forEach(task => {
            task.apply(null, arguments)
        })
    }
}


const hook = new SyncHook(['name'])
hook.tap('node', function(name) {
  console.log(`${name}, node is interesting`)
})
hook.tap('js', function(name) {
  console.log(`${name}, js is interesting`)
})
hook.call('jlq')
// jlq, node is interesting
// jlq, js is interesting
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, callback) {
        this.tasks.push(callback)
    }
    call() {
        // for循环写法
        // const len = this.tasks.length
        // for(let i = 0; i < len; i++) {
        //     const re = this.tasks[i].apply(null, arguments)
        //     if (re !== undefined) break
        // }

        // do while写法
        let ret, len = 0
        do {
            ret = this.tasks[len++].apply(null, arguments)
        } while (ret === undefined && len < this.tasks.length)
    }
}

const hook = new SyncBailHook(['name'])
hook.tap('js', function(name) {
    console.log(`${name}, js学完了`)
    return undefined
})
hook.tap('node', function(name) {
    console.log(`${name}, node学完了`)
    return '我太难了,学不动了'
})
hook.tap('webGL', function(name) {
    console.log(`${name}, webGL学完了`)
})
hook.call('jlq')
// jlq, js学完了
// jlq, node学完了
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

# SyncWaterfallHook

同步瀑布钩子。依次执行注册的监听函数,把执行结果传给下一个回调,作为其参数继续执行。

class SyncWaterfallHook {
    constructor() {
        this.tasks = []
    }
    tap(name, callback) {
        this.tasks.push(callback)
    }
    call(...args) {
        // let firstCallback = this.tasks.shift()
        // let ret = firstCallback(...args)
        // this.tasks.forEach(task => {
        //     ret = task(ret)
        // })

        // 解构赋值 + reduce
        let [firstCallback, ...otherCallback] = this.tasks
        otherCallback.reduce((pre, cur) => {
            return cur(pre)
        }, firstCallback(...args))
    }
}


const hook = new SyncWaterfallHook(['name'])
hook.tap('node', function(name) {
    console.log(`${name}开始学习node了`)
    return 'node'
})
hook.tap('js', function(name) {
    console.log(`${name}学完了,开始学习js`)
})
hook.call('jlq')
// jlq开始学习node了
// node学完了,开始学习js
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

# SyncLoopHook

同步循环钩子。依次执行监听函数,如果返回的不是undefined,就反复执行该回调。

class SyncLoopHook {
    constructor() {
        this.tasks = []
    }
    tap(name, callback) {
        this.tasks.push(callback)
    }
    call(...args) {
        this.tasks.forEach(task => {
            let ret
            do {
                ret = task(...args)
            } while (ret !== undefined)
        })
    }
}


const hook = new SyncLoopHook(['name'])
let index = 1
hook.tap('node', function(name) {
    console.log(`${name}学习node的第${index++}`)
    return index === 5 ? undefined : '继续学习node'
})
hook.tap('js', function(name) {
    console.log(`${name},node总共学了${index}天,开始学习js`)
})
hook.call('jlq')
// jlq学习node的第1天
// jlq学习node的第2天
// jlq学习node的第3天
// jlq学习node的第4天
// jlq,node总共学了5天,开始学习js
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

# AsyncParallelHook

异步并行钩子。异步执行所有监听函数,全部执行完后调用指定回调。

# async写法

class AsyncParallelHook {
    constructor() {
        this.tasks = []
    }
    tapAsync(name, callback) {
        this.tasks.push(callback)
    }
    callAsync() {
        const args = Array.prototype.slice.call(arguments, 0)
        const cb = args.pop()
        let index = 0

        const fn = () => {
            index++
            if (index === this.tasks.length) cb()
        }
        this.tasks.forEach(task => {
            task.call(null, ...args, fn)
        })
    }
}

const hook = new AsyncParallelHook(['name'])
hook.tapAsync('node', function(name, cb) {
    setTimeout(() => {
        console.log(`${name}一边学node`)
        cb()
    }, 2000)
})
hook.tapAsync('java', function(name, cb) {
    setTimeout(() => {
        console.log(`${name}一边学java`)
        cb()
    }, 1000)
})
hook.callAsync('jlq', function() {
    console.log('学完了')
})
// 1s后输出:jlq一边学java
// 再过1s后输出:jlq一边学node
// 学完了
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

# promise写法

class AsyncParallelHook {
    constructor() {
        this.tasks = []
    }
    tapPromise(name, callback) {
        this.tasks.push(callback)
    }
    promise() {
        const promiseAll = this.tasks.map(task => task.apply(null, arguments))
        return Promise.all(promiseAll)
    }
}

const hook = new AsyncParallelHook(['name'])
hook.tapPromise('node', function(name) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve()
            console.log(`${name}一边学node`)
        }, 2000)
    })
})
hook.tapPromise('java', function(name) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve()
            console.log(`${name}一边学java`)
        }, 1000)
    })
})
hook.promise('jlq').then(res => {
    console.log('学完了')
})
// 1s后输出:jlq一边学java
// 再过1s后输出:jlq一边学node
// 学完了
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

# AsyncSeriesHook

异步系列钩子。按顺序依次异步执行监听函数(需要等上一个回调执行完),执行完成后调用指定回调。

# async写法

class AsyncSeriesHook {
    constructor() {
        this.tasks = []
    }
    tapAsync(name, callback) {
        this.tasks.push(callback)
    }
    callAsync(...args) {
        const finalClaaback = args.pop()
        let index = 0

        const next = () => {
            if (index === this.tasks.length) return finalClaaback()
            this.tasks[index++](...args, next)
        }
        next()
    }
}

const hook = new AsyncSeriesHook(['name'])
hook.tapAsync('node', function(name, cb) {
    setTimeout(() => {
        console.log(`${name}先学习node`)
        cb()
    }, 2000)
})
hook.tapAsync('java', function(name, cb) {
    setTimeout(() => {
        console.log(`${name}接着学习java`)
        cb()
    }, 1000)
})
hook.callAsync('jlq', function() {
    console.log('都学完了')
})
// 2s后打印:jlq先学习node
// 再过1s后打印:jlq接着学习java
// 都学完了
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

# promise写法

class AsyncSeriesHook {
    constructor() {
        this.tasks = []
    }
    tapPromise(name, callback) {
        this.tasks.push(callback)
    }
    promise(...args) {
        let [first, ...other] = this.tasks
        return other.reduce((pre, cur) => {
            return pre.then(res => cur(...args))
        }, first(...args))
    }
}

const hook = new AsyncSeriesHook(['name'])
hook.tapPromise('node', function(name) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve()
            console.log(`${name}先学习node`)
        }, 2000)
    })
})
hook.tapPromise('java', function(name) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve()
            console.log(`${name}接着学习java`)
        }, 1000)
    })
})
hook.promise('jlq').then(res => {
    console.log('都学完了')
})
// 2s后打印:jlq先学习node
// 再过1s后打印:jlq接着学习java
// 都学完了
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

# AsyncSeriesWaterfallHook

异步系列瀑布钩子。依次异步执行监听函数,将上一个钩子的结果传给下一个钩子,全部执行完成后执行指定回调。

class AsyncSeriesWaterfallHook {
    constructor() {
        this.tasks = []
    }
    tapAsync(name, callback) {
        this.tasks.push(callback)
    }
    callAsync(...args) {
        const finalCallback = args.pop()
        let index = 0
        const cb = (err, ...result) => {
            const task = this.tasks[index]
            if (err !== null || !task) return finalCallback()
            this.tasks[index](...result, cb)
            index++
        }
        cb(null, ...args)
    }
}

const hook = new AsyncSeriesWaterfallHook(['name'])
hook.tapAsync('node', function(name, cb) {
    setTimeout(() => {
        console.log(`${name}开始学习node`)
        cb(null, 'node学完了,')
    }, 2000)
})
hook.tapAsync('java', function(name, cb) {
    setTimeout(() => {
        console.log(`${name}开始学习java`)
        cb(null, 'java学完了')
    }, 1000)
})
hook.callAsync('jlq', function() {
    console.log('全部学完了')
})
// 2s后打印:jlq开始学习node
// 1s后打印:node学完了,开始学习java
// 全部学完了
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