AsyncSeriesHook 异步串行钩子
本节来学习异步串行钩子。
上一节的异步并行钩子,是所有任务执行完之后,再执行最终的回调。
异步串行钩子是,第一个任务执行完,再执行第二个,依次执行,执行完最后一个后再执行最终的回调。
(有点像express中的源码,执行完一个再执行另外一个)
AsyncSeriesHook(回调形式)
tapable中的AsyncSeriesHook (回调形式)
js
// webpack\webpack-tapable\7.start.js
// 异步串行钩子 AsyncSeriesHook tapable
// 第一个任务执行完,再执行第二个,依次执行,执行完最后一个后再执行最终的回调。
/*
三种注册任务的方法
1. tap 注册,同步注册,注册的是同步任务,依次执行
2. tapAsync 注册,异步注册,注册的是异步任务,会有一个回调,当异步任务执行之后,同时执行下这个回调,标识当前异步任务执行成功
如果,其中有一个回调未执行,则最终的执行成功的回调永远不会执行
3. tapPromise 注册,异步注册,注册的是异步任务,通过promise封装的
*/
let { AsyncSeriesHook } = require('tapable')
class Lesson {
// new 对象时 构造函数会被执行
constructor() {
// new对象的时候 注册一些钩子
// 一旦new一个新的实例时,这些钩子就被启用了
this.hook = {
// 假如有个架构课
arch: new AsyncSeriesHook(['name']), // 可能会传一个参数,参数是可选的
}
}
// 用来注册的方法
tap() { // 注册监听函数
// 使用 tapAsync 注册,回调 的形式
this.hook.arch.tapAsync('vue', (name, cb) => {
setTimeout(() => {
console.log('vue 111', name);
cb()
}, 1000)
})
this.hook.arch.tapAsync('react', (name, cb) => {
setTimeout(() => {
console.log('react 111', name);
cb()
}, 1000)
})
// 调用这个方法时,会将上面两个事件注册到数组中
}
// 启动钩子的方法
start() {
// 执行之前注册的事件
this.hook.arch.callAsync('cheny', () => {
// 最后一个异步任务执行成功之后,执行
console.log('end');
})
}
}
// 测试
let l = new Lesson() // 声明一个实例
// 注册两个事件
l.tap()
// 调用start方法 启动钩子
l.start()
先执行vue,一秒后,再输出react,然后输出end
自己实现AsyncSeriesHook(回调形式)
js
// webpack\webpack-tapable\7.case.js
// 异步任务,注册到一个数组中
// 串行执行异步任务
// 执行完一个,再执行下一个,所有执行完之后,再去执行最终的成功回调
class AsyncSeriesHook { // 异步串行钩子
constructor(args) { // args -> ['name']
this.tasks = [] // 保存注册的事件
}
// 异步注册事件
tapAsync(name, task) {
this.tasks.push(task)
}
// 执行回调
// 一个一个执行,第一个执行完之后,再执行下一个
callAsync(...args) {
// 拿出最终的回调
let finalCallback = args.pop()
let index = 0; // 当前执行到第几个任务了
let next = () => {
// 出口 index 正好越界的时候,代表所有的任务都执行完毕了
if (index === this.tasks.length) {
return finalCallback()
}
let task = this.tasks[index] // 取出数组中注册的任务
// 取出完之后 让index++,便于获取下一个任务
index++
task(...args, next) // 这里的回调就传递next,正好一个一个执行,执行完一个之后,执行下一个
// 非常像 express 的原理了
}
// 先执行一次
next()
}
}
let hook = new AsyncSeriesHook(['name']) // 参数可以传可以不传
// 注册异步事件 tapAsync
hook.tapAsync('vue', function (name, cb) {
setTimeout(() => {
console.log('vue 1111', name);
cb()
}, 1000)
})
hook.tapAsync('react', function (name, cb) {
setTimeout(() => {
console.log('react 1111', name);
cb()
}, 1000)
})
// 异步任务执行,使用 callAsync 方法
hook.callAsync('cheny', () => {
console.log('end');
})
先执行vue,一秒后,再输出react,然后输出end
AsyncSeriesHook(promise 形式)
tapable中的AsyncSeriesHook (promise 形式)
js
// webpack\webpack-tapable\8.start.js
// 异步串行钩子 AsyncSeriesHook tapable
// 第一个任务执行完,再执行第二个,依次执行,执行完最后一个后再执行最终的回调。
/*
三种注册任务的方法
1. tap 注册,同步注册,注册的是同步任务,依次执行
2. tapAsync 注册,异步注册,注册的是异步任务,会有一个回调,当异步任务执行之后,同时执行下这个回调,标识当前异步任务执行成功
如果,其中有一个回调未执行,则最终的执行成功的回调永远不会执行
3. tapPromise 注册,异步注册,注册的是异步任务,通过promise封装的
*/
let { AsyncSeriesHook } = require('tapable')
class Lesson {
// new 对象时 构造函数会被执行
constructor() {
// new对象的时候 注册一些钩子
// 一旦new一个新的实例时,这些钩子就被启用了
this.hook = {
// 假如有个架构课
arch: new AsyncSeriesHook(['name']), // 可能会传一个参数,参数是可选的
}
}
// 用来注册的方法
tap() { // 注册监听函数
// 使用 tapPromise 注册,promise 的形式
this.hook.arch.tapPromise('vue', (name) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('vue 111', name);
resolve()
}, 1000)
})
})
this.hook.arch.tapPromise('react', (name) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('react 111', name);
resolve()
}, 1000)
})
})
// 调用这个方法时,会将上面两个事件注册到数组中
}
// 启动钩子的方法
start() {
// 执行之前注册的事件
this.hook.arch.promise('cheny').then(() => {
// 最后一个异步任务执行成功之后,执行
console.log('end');
})
}
}
// 测试
let l = new Lesson() // 声明一个实例
// 注册两个事件
l.tap()
// 调用start方法 启动钩子
l.start()
先执行vue,再执行react,最后输出end
自己实现AsyncSeriesHook(promise形式)
js
// webpack\webpack-tapable\8.case.js
// 异步任务,注册到一个数组中
// 串行执行异步任务
// 执行完一个,再执行下一个,所有执行完之后,再去执行最终的成功回调
class AsyncSeriesHook { // 异步串行钩子
constructor(args) { // args -> ['name']
this.tasks = [] // 保存注册的事件
}
// 异步注册事件
tapPromise(name, task) {
this.tasks.push(task)
}
// 执行回调
// 一个一个执行,第一个执行完之后,再执行下一个
promise(...args) {
// 需要把注册的promise串联起来
let [first, ...others] = this.tasks
return others.reduce((pre, cur) => {
return pre.then(() => { // 当前promise执行成功之后,再执行下一个promise
return cur(...args)
})
}, first(...args))
}
}
let hook = new AsyncSeriesHook(['name']) // 参数可以传可以不传
// 注册异步事件 tapPromise
hook.tapPromise('vue', function (name) {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('vue 1111', name);
resolve()
}, 1000)
})
})
hook.tapPromise('react', function (name) {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('react 1111', name);
resolve()
}, 1000)
})
})
// 异步任务执行,使用 promise 方法
hook.promise('cheny').then(() => {
console.log('end');
})
总结
异步并行钩子,是所有任务执行完之后,再执行最终的回调。
异步串行钩子是,第一个任务执行完,再执行第二个,依次执行,执行完最后一个后再执行最终的回调。
AsyncSeriesHook 异步串联(回调版本)
注册
js// 注册异步事件 tapAsync hook.tapAsync('vue', function (name, cb) { setTimeout(() => { console.log('vue 1111', name); cb() }, 1000) }) hook.tapAsync('react', function (name, cb) { setTimeout(() => { console.log('react 1111', name); cb() }, 1000) })
调用
js// 异步任务执行,使用 callAsync 方法 hook.callAsync('cheny', () => { console.log('end'); })
核心代码
js// 执行回调 // 一个一个执行,第一个执行完之后,再执行下一个 callAsync(...args) { // 拿出最终的回调 let finalCallback = args.pop() let index = 0; // 当前执行到第几个任务了 let next = () => { // 出口 index 正好越界的时候,代表所有的任务都执行完毕了 if (index === this.tasks.length) { return finalCallback() } let task = this.tasks[index] // 取出数组中注册的任务 // 取出完之后 让index++,便于获取下一个任务 index++ task(...args, next) // 这里的回调就传递next,正好一个一个执行,执行完一个之后,执行下一个 // 非常像 express 的原理了 } // 先执行一次 next() }
AsyncSeriesHook 异步串联(promise版本)
注册
js// 注册异步事件 tapPromise hook.tapPromise('vue', function (name) { return new Promise((resolve, reject) => { setTimeout(() => { console.log('vue 1111', name); resolve() }, 1000) }) }) hook.tapPromise('react', function (name) { return new Promise((resolve, reject) => { setTimeout(() => { console.log('react 1111', name); resolve() }, 1000) }) })
调用
js// 异步任务执行,使用 promise 方法 hook.promise('cheny').then(() => { console.log('end'); })
核心代码
js// 执行回调 // 一个一个执行,第一个执行完之后,再执行下一个 promise(...args) { // 需要把注册的promise串联起来 let [first, ...others] = this.tasks return others.reduce((pre, cur) => { return pre.then(() => { // 当前promise执行成功之后,再执行下一个promise return cur(...args) }) }, first(...args)) }
参考
https://www.bilibili.com/video/BV1a4411e7Bz?p=31&spm_id_from=pageDriver