Skip to content

AsyncSeriesWaterfallHook 异步串行瀑布钩子

假如异步之间有关系,就可以使用这个钩子。

异步串行瀑布钩子,上一个异步的结果,会是下一个异步的参数。

AsyncSeriesWaterfallHook

tapable中的 AsyncSeriesWaterfallHook (回调格式)

js
// webpack\webpack-tapable\9.start.js

// 异步串行瀑布钩子 AsyncSeriesWaterfallHook tapable
// 第一个任务执行完的结果,是第二个任务的参数,都是异步任务
/* 
    三种注册任务的方法
    1. tap 注册,同步注册,注册的是同步任务,依次执行
    2. tapAsync 注册,异步注册,注册的是异步任务,会有一个回调,当异步任务执行之后,同时执行下这个回调,标识当前异步任务执行成功
        如果,其中有一个回调未执行,则最终的执行成功的回调永远不会执行
    3. tapPromise 注册,异步注册,注册的是异步任务,通过promise封装的
*/
let { AsyncSeriesWaterfallHook } = require('tapable')

class Lesson {
    // new 对象时 构造函数会被执行
    constructor() {
        // new对象的时候 注册一些钩子
        // 一旦new一个新的实例时,这些钩子就被启用了
        this.hook = {
            // 假如有个架构课
            arch: new AsyncSeriesWaterfallHook(['name']), // 可能会传一个参数,参数是可选的
        }
    }

    // 用来注册的方法
    tap() { // 注册监听函数
        // 使用 tapAsync 注册
        this.hook.arch.tapAsync('vue', (name, cb) => {
            setTimeout(() => {
                console.log('vue 111', name);
                cb(null, 'vue 学的还不错') // 回调中的第一个参数是错误,如果有错误的下一个就不会执行了
                // 第一个任务的执行结果,是下一个任务的参数
            }, 1000)
        })

        this.hook.arch.tapAsync('react', (data, cb) => {
            setTimeout(() => {
                console.log('react 111', data);
                cb(null, 'react 学的还不错')
            }, 1000)
        })

        this.hook.arch.tapAsync('webpack', (data, cb) => {
            setTimeout(() => {
                console.log('webpack 111', data);
                cb(null)
            }, 1000)
        })

        // 调用这个方法时,会将上面两个事件注册到数组中
    }

    // 启动钩子的方法
    start() {
        // 执行之前注册的事件
        this.hook.arch.callAsync('cheny', () => {
            // 最后一个异步任务执行成功之后,执行
            console.log('end');
        })
    }
}

// 测试
let l = new Lesson() // 声明一个实例

// 注册两个事件
l.tap()

// 调用start方法 启动钩子
l.start()

注册任务时,每个回调传的第一个参数是错误,如果没有错误时,会继续执行下一个任务,下一个任务的参数是上一个任务的执行结果。

image-20220201094740145

自己实现的 AsyncSeriesWaterfallHook (回调格式)

js
// webpack\webpack-tapable\9.case.js

// 异步任务,注册到一个数组中
// 串行执行异步任务
// 执行完一个,再执行下一个
// 下一个任务的参数 是上一个任务的执行结果
// 假如上一个任务执行出错,就跳过下一个任务,直接执行最终的回调
class AsyncSeriesWaterfallHook { // 异步串行瀑布钩子

    constructor(args) { // args -> ['name']
        this.tasks = [] // 保存注册的事件
    }

    // 异步注册事件
    tapAsync(name, task) {
        this.tasks.push(task)
    }

    // 执行回调
    // 一个一个执行,第一个执行完之后,再执行下一个
    // 下一个任务的参数 是上一个任务的执行结果
    // 假如上一个任务执行出错,就跳过下一个任务,直接执行最终的回调
    callAsync(...args) {
        // 拿出最终的回调
        let finalCallback = args.pop()

        let index = 0; // 当前执行到第几个任务了

        let next = (err, data) => {

            // 出口 index 正好越界的时候,代表所有的任务都执行完毕了
            if (index === this.tasks.length) {
                return finalCallback()
            }

            let task = this.tasks[index] // 取出数组中注册的任务

            // 如果执行的是第一个
            if (index === 0) {
                task(...args, next)
            } else { // 如果不是第一个,第一个参数就上一个参数的结果
                task(data, next)
            }

            // 取出完之后 让index++,便于获取下一个任务
            index++
        }

        // 先执行一次
        next()

    }
}


let hook = new AsyncSeriesWaterfallHook(['name']) // 参数可以传可以不传

// 注册异步事件 tapAsync
hook.tapAsync('vue', function (name, cb) {
    setTimeout(() => {
        console.log('vue 1111', name);
        cb(null, 'vue学的还不错')
    }, 1000)


})
hook.tapAsync('react', function (data, cb) {
    setTimeout(() => {
        console.log('react 1111', data);
        cb(null, 'react学的还不错')
    }, 1000)
})

hook.tapAsync('webpack', function (data, cb) {
    setTimeout(() => {
        console.log('webpack 1111', data);
        cb(null)
    }, 1000)
})

// 异步任务执行,使用 callAsync 方法
hook.callAsync('cheny', () => {
    console.log('end');
})

image-20220201100032319

总结

异步串行瀑布钩子,AsyncSeriesWaterfallHook,任务之间有相互依赖关系时使用。

下一个任务的参数,是上一个任务的执行结果。依次执行。

假如有个任务出错了,那么就不会继续执行下面的任务了,直接执行最终的回调。

  1. 注册

    js
    // 注册异步事件 tapAsync
    hook.tapAsync('vue', function (name, cb) {
        setTimeout(() => {
            console.log('vue 1111', name);
            cb(null, 'vue学的还不错')
        }, 1000)
    
    
    })
    hook.tapAsync('react', function (data, cb) {
        setTimeout(() => {
            console.log('react 1111', data);
            cb(null, 'react学的还不错')
        }, 1000)
    })
    
    hook.tapAsync('webpack', function (data, cb) {
        setTimeout(() => {
            console.log('webpack 1111', data);
            cb(null)
        }, 1000)
    })
  2. 使用

    js
    // 异步任务执行,使用 callAsync 方法
    hook.callAsync('cheny', () => {
        console.log('end');
    })
  3. 核心代码

    js
    // 执行回调
    // 一个一个执行,第一个执行完之后,再执行下一个
    // 下一个任务的参数 是上一个任务的执行结果
    // 假如上一个任务执行出错,就跳过下一个任务,直接执行最终的回调
    callAsync(...args) {
        // 拿出最终的回调
        let finalCallback = args.pop()
        let index = 0; // 当前执行到第几个任务了
        let next = (err, data) => {
            // 出口 index 正好越界的时候,代表所有的任务都执行完毕了
            if (index === this.tasks.length) {
                return finalCallback()
            }
            let task = this.tasks[index] // 取出数组中注册的任务
            // 如果执行的是第一个
            if (index === 0) {
                task(...args, next)
            } else { // 如果不是第一个,第一个参数就上一个参数的结果
                task(data, next)
            }
            // 取出完之后 让index++,便于获取下一个任务
            index++
        }
        // 先执行一次
        next()
    }

参考

https://www.bilibili.com/video/BV1a4411e7Bz?p=32