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()
注册任务时,每个回调传的第一个参数是错误,如果没有错误时,会继续执行下一个任务,下一个任务的参数是上一个任务的执行结果。
自己实现的 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');
})
总结
异步串行瀑布钩子,AsyncSeriesWaterfallHook,任务之间有相互依赖关系时使用。
下一个任务的参数,是上一个任务的执行结果。依次执行。
假如有个任务出错了,那么就不会继续执行下面的任务了,直接执行最终的回调。
注册
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) })
使用
js// 异步任务执行,使用 callAsync 方法 hook.callAsync('cheny', () => { console.log('end'); })
核心代码
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() }