【tapable】tapable hooks基本使用

const {
	SyncHook,
	SyncBailHook,
	SyncWaterfallHook,
	SyncLoopHook,
	AsyncParallelHook,
	AsyncParallelBailHook,
	AsyncSeriesHook,
	AsyncSeriesBailHook,
	AsyncSeriesWaterfallHook
 } = require("tapable");

SyncHook

按注册顺序同步执行,无法中断

const SyncHook =require( './lib/SyncHook')
const hooks ={
    sync:new SyncHook(['t1','t2'])
}
hooks.sync.tap('sync',(...args)=>{
    console.log('sync',...args)
})
hooks.sync.tap('sync',(...args)=>{
    console.log('sync1',...args)
})
hooks.sync.tap('sync2',(...args)=>{
    console.log('sync2',...args)
})
hooks.sync.call('a','b')

输出

sync a b
sync1 a b
sync2 a b

SyncBailHook

按注册顺序同步执行,当回调函数返回非undefined值时中断后续回调操作

hooks.syncBai.tap('syncBai',(...args)=>{
    console.log('syncBai',...args)
})
hooks.syncBai.tap('syncBai',(...args)=>{
    console.log('syncBai1',...args)
    return 'syncBai1'
})
hooks.syncBai.tap('syncBai2',(...args)=>{
    console.log('syncBai2',...args)
    return 3
})
hooks.syncBai.call('a','b')
hooks.syncBai.callAsync('a','b',()=>{
    console.log('syncBai.callAsync')
})

输出

syncBai a b
syncBai1 a b
syncBai a b
syncBai1 a b
syncBai.callAsync

SyncWaterfallHook

按注册顺序同步执行,回调函数返回值为下一个回调函数的入参

hooks.syncWaterfall.tap('syncWaterfall',(...args)=>{
    console.log('syncWaterfall:',...args)
    return 'syncWaterfall'
})
hooks.syncWaterfall.tap('syncWaterfall',(...args)=>{
    console.log('syncWaterfall1:',...args)
    return 'syncWaterfall1'
})
hooks.syncWaterfall.tap('syncWaterfall2',(...args)=>{
    console.log('syncWaterfall2:',...args)
})
hooks.syncWaterfall.call('a','b')
hooks.syncWaterfall.callAsync('a','b',()=>{
    console.log('syncWaterfall.callAsync')
})

结果

syncWaterfall: a b
syncWaterfall1: syncWaterfall b
syncWaterfall2: syncWaterfall1 b
syncWaterfall: a b
syncWaterfall1: syncWaterfall b
syncWaterfall2: syncWaterfall1 b
syncWaterfall.callAsync

SyncLoopHook

按注册顺序同步执行,回调函数返回undefined时中断队列循环,进入下一个回调函数开始队列循环,(重点:一定要中断后才能进入下一个回调,且下一回调执行完后,如果未循环,则从第一个注册函数开始再一次顺序执行)

hooks.syncLoop.tap('syncLoop',(...args)=>{
    console.log('syncLoop:',...args)
    return  ++counter.a < 2 ? 'syncLoop':undefined
})
hooks.syncLoop.tap('syncLoop',(...args)=>{
    console.log('syncLoop1:',...args)
    return  ++counter.b < 3 ? 'syncLoop1':undefined
})
hooks.syncLoop.tap('syncLoop2',(...args)=>{
    console.log('syncLoop2:',...args)
    return  ++counter.c < 4 ? 'syncLoop1':undefined
})
hooks.syncLoop.call('a','b')
hooks.syncLoop.callAsync('a','b',()=>{
    console.log('syncLoop.callAsync')
})

结果

syncLoop: a b
syncLoop: a b
syncLoop1: a b
syncLoop: a b
syncLoop1: a b
syncLoop: a b
syncLoop1: a b
syncLoop2: a b
syncLoop: a b
syncLoop1: a b
syncLoop2: a b
syncLoop: a b
syncLoop1: a b
syncLoop2: a b
syncLoop: a b
syncLoop1: a b
syncLoop2: a b
syncLoop: a b
syncLoop1: a b
syncLoop2: a b
syncLoop.callAsync

为了方便后面异步测试,做了几个工具函数



const time = Date.now()

const log=(...args)=>{
    console.log(Date.now()-time,...args)
}

const rejectKey = Symbol('reject')

const handlerFactory = (tap)=>{
    return (value,time=0)=>{
        let tapHandler = (...args)=>{
            log(value+'.'+tap,...args)
            return value && value.indexOf('return')> -1 ? (value+'.'+tap):undefined
        }
        let tapAsyncHandler =(...args)=>{
            const cb= args.pop()
            tapHandler(...args);
            setTimeout(()=>{
                tapHandler(...['async.end',...args]);
                cb(value?value+'.'+tap:undefined)
            },time)
            return value && value.indexOf('return')> -1 ? (value+'.'+tap):undefined
        }
        let tapPromiseHandler =(...args)=>{
            return new Promise((resolve,reject)=>{
                let handler = resolve
                if(args[0] === rejectKey){
                    args.shift()
                    handler = reject
                }
                args.push(handler)
                tapAsyncHandler(...args)
            })
        }
        if(tap==='tap') return tapHandler
        if(tap==='tapAsync') return tapAsyncHandler
        if(tap==='tapPromise') return tapPromiseHandler
    }
}
const factory=(tap,hook)=>{
    const handler = handlerFactory(tap)
    if(tap==='tap') return (...args)=> hook.tap('tap',handler(...args))
    if(tap==='tapAsync') return (...args)=> hook.tapAsync('tap',handler(...args))
    if(tap==='tapPromise')   {
        const tapPromise =(...args)=> hook.tapPromise('tap',handler(...args))
        tapPromise.reject = (...args)=> hook.tapPromise('tap',handler(...args).bind(null,rejectKey))
        return tapPromise
    }
}



AsyncParallelHook

按注册顺序同步执行事件函数,若没有tapPromise或tapAsync异步注册时,callAsync回调同步执行,反之,当回调函数异步执行完成:
1、成功状态返回值不为undefined时(tapAsync 中callback(result)tapPromise中resolve(result),则执行callAsync回调,并将返回值作为callAsync的参数值
2、失败状态返任何值(tapPromise中resolve(result)),则执行callAsync回调,并将返回值作为callAsync的参数值

在这个点上纠结了很久,callAsync回调时机及回调参数值的问题,后来看了源码发现,callAsync的第一个参数值代表异常信息(所以有空多看源码cry…),执行任务中出现任何一个异常时(reject,callback非空值)时,则执行callAsync,否则当所有任务执行完成时,调用callAsync

const tap= factory('tap',hooks.asyncParallel)
const tapAsync= factory('tapAsync',hooks.asyncParallel)
const tapPromise= factory('tapPromise',hooks.asyncParallel)
tap('1')
tapAsync('2',2000);
tapAsync('3',5000);
tapPromise('x',3000)
tap('5')
tapAsync('6',5000)

hooks.asyncParallel.callAsync('a','b',(...args)=>{
    log('asyncParallel.callAsync',args)
})

结果

2 '1.tap' 'a' 'b'
3 '2.tapAsync' 'a' 'b'
4 '3.tapAsync' 'a' 'b'
5 'x.tapPromise' 'a' 'b'
5 '5.tap' 'a' 'b'
5 '6.tapAsync' 'a' 'b'
2010 '2.tapAsync' 'async.end' 'a' 'b'
2010 'asyncParallel.callAsync' [ '2.tapAsync' ]
3007 'x.tapPromise' 'async.end' 'a' 'b'
5007 '3.tapAsync' 'async.end' 'a' 'b'
5007 '6.tapAsync' 'async.end' 'a' 'b'

若此时将tapAsync(‘2’,2000);改为tapAsync(undefined,2000);
结果

2 '1.tap' 'a' 'b'
3 'undefined.tapAsync' 'a' 'b'
4 '3.tapAsync' 'a' 'b'
4 'x.tapPromise' 'a' 'b'
4 '5.tap' 'a' 'b'
4 '6.tapAsync' 'a' 'b'
2008 'undefined.tapAsync' 'async.end' 'a' 'b'
3009 'x.tapPromise' 'async.end' 'a' 'b'
5006 '3.tapAsync' 'async.end' 'a' 'b'
5007 'asyncParallel.callAsync' [ '3.tapAsync' ]
5009 '6.tapAsync' 'async.end' 'a' 'b'

AsyncParallelBailHook

从名字上看,带了一个特殊标识Bai,依此推断应该与SyncBailHook有类似的功能,而Parallel应该代表与AsyncParallelHook类似

测试1:

tap('return.1') // 带return 表示回调于返回打印值
tapPromise.reject('2',3000)
tapAsync('3',2000);
tapAsync('return.5',1000);
tap('2')
tapPromise.reject('x',2000)
tap('5')
tapAsync('6',5000)
tapPromise('7',1000)
hooks.asyncParallelBail.callAsync('a','b',(...args)=>{
    log('asyncParallelBail.callAsync',args)
})

打印结果 如下,可看出,当返回非undfined值是中断后续事件函数执行,callAsync执行参数值为tap返回值

1 return.1.tap a b
9 asyncParallelBail.callAsync [ null, 'return.1.tap' ]

测试2:


tap('1')
tapPromise(undefined,3000)
tapPromise('2',3000)
tapPromise.reject(undefined,3000)
tapPromise.reject('reject',3000)
tapAsync('3',2000);
tapAsync(undefined,1000);
tapAsync('return.3',1000);
tap('return.2')
tapPromise.reject('x',2000)
tap('5')
tapAsync('6',5000)
tapPromise('7',1000)
hooks.asyncParallelBail.callAsync('a','b',(...args)=>{
    log('asyncParallelBail.callAsync',args)
})

打印结果

1 1.tap a b
8 undefined.tapPromise a b
8 2.tapPromise a b
8 undefined.tapPromise a b
8 reject.tapPromise a b
8 3.tapAsync a b
8 undefined.tapAsync a b
8 return.3.tapAsync a b
8 return.2.tap a b
1011 undefined.tapAsync async.end a b
1012 return.3.tapAsync async.end a b
2009 3.tapAsync async.end a b
3010 undefined.tapPromise async.end a b
3010 2.tapPromise async.end a b
3010 asyncParallelBail.callAsync [ null, '2.tapPromise' ]
3011 undefined.tapPromise async.end a b
3011 reject.tapPromise async.end a b

========== 直接说结论==========

当只有tap事件返回非空值时,才会中断后续所有事件执行,且callAsync参数值为返回值,其余不会中断后续事件执行

callAsync的执行时机在于事件函数最先返回非空值,注意,此最先不代表事件执行时间的最短,而是完成时当前事件之前所有事件均已完成。
如上述例子:tapAsync(‘return.3’,1000);是最先完成的一批事件,但此时

tapPromise(undefined,3000)
tapPromise('2',3000)
tapPromise.reject(undefined,3000)
tapPromise.reject('reject',3000)
tapAsync('3',2000);

以上任务均还未完成,所以callAsync未执行而且是等tapPromise(‘2’,3000)完成后才执行

asyncSeries

异步串行,即上一个任务完成后,再执行下一个任务,若任务异常(reject,callback非空值),则中断任务


const tap= factory('tap',hooks.asyncSeries)
const tapAsync= factory('tapAsync',hooks.asyncSeries)
const tapPromise= factory('tapPromise',hooks.asyncSeries)
tap('return.1')
tapAsync(undefined,2000);
tapPromise('3000',3000)
tapPromise.reject('3',4000)
tapAsync(undefined,5000);
tapPromise('1000',1000)
tap('5')
tapAsync('6',500)
hooks.asyncSeries.callAsync('a','b',(...args)=>{
    log('asyncSeries.callAsync',args)
})

打印结果

2 'return.1.tap' 'a' 'b'
5 'undefined.tapAsync' 'a' 'b'
2009 'undefined.tapAsync' 'async.end' 'a' 'b'
2010 '3000.tapPromise' 'a' 'b'
5013 '3000.tapPromise' 'async.end' 'a' 'b'
5014 '3.tapPromise' 'a' 'b'
9017 '3.tapPromise' 'async.end' 'a' 'b'
9017 'asyncSeries.callAsync' [ 'reject.3.tapPromise' ]

AsyncSeriesBailHook

带了Bai,按上文理解,此处应该可以用返回值中断事件执行

const tap =  factory('tap',hooks.asyncSeriesBail)
const tapAsync= factory('tapAsync',hooks.asyncSeriesBail)
const tapPromise= factory('tapPromise',hooks.asyncSeriesBail)
tap('return.1')
tapAsync(undefined,2000);
tapPromise('3000',3000)
tapPromise.reject('3',4000)

打印结果

2 'return.1.tap' 'a' 'b'
4 'asyncSeriesBail.callAsync' [ null, 'return.1.tap' ]

确实返回非空值中断后续事件执行,感觉就是asyncSeries+SyncBailHook的组件
但…看看下一个例子

const tap =  factory('tap',hooks.asyncSeriesBail)
const tapAsync= factory('tapAsync',hooks.asyncSeriesBail)
const tapPromise= factory('tapPromise',hooks.asyncSeriesBail)
tap('1')
tapAsync(undefined,2000);
tapPromise('3000',3000)
tapPromise.reject('3',4000)
tapAsync(undefined,5000);
tapPromise('1000',1000)
tap('5')
tapAsync('6',500)
hooks.asyncSeriesBail.callAsync('a','b',(...args)=>{
    log('asyncSeriesBail.callAsync',args)
})

打印结果

2 '1.tap' 'a' 'b'
5 'undefined.tapAsync' 'a' 'b'
2011 'undefined.tapAsync' 'async.end' 'a' 'b'
2012 '3000.tapPromise' 'a' 'b'
5014 '3000.tapPromise' 'async.end' 'a' 'b'
5014 'asyncSeriesBail.callAsync' [ null, 'resolve.3000.tapPromise' ]

callAsync的执行回调结果
5014 'asyncSeriesBail.callAsync' [ null, 'resolve.3000.tapPromise' ]
上文提及异常是不包含resolve的,但在此函数中,tapPromise也可以通过resolve非空值中断事件执行

AsyncSeriesWaterfallHook

Series代表事件串行,Waterfall根据上文来为上一事件返回值做为下一事件输入值

事件执行异常,中断后续事件执行,在此函数中,异常情况为(reject、callback非空值),Promise中resolve成功返回非空值将作为参传入事件中函数,callback回调函数第二个参数非空时作为参传入事件中函数

如下:

const tap =  factory('tap',hooks.asyncSeriesWaterfall)
const tapAsync= factory('tapAsync',hooks.asyncSeriesWaterfall)
const tapPromise= factory('tapPromise',hooks.asyncSeriesWaterfall)
tap('1')
tapAsync('2',2000);
tapAsync('3',2000);
tapPromise('3000',3000)
// tapPromise.reject('3',4000)
tapAsync(undefined,5000);
tapPromise('1000',1000)
tap('5')
tapAsync('6',500)
hooks.asyncSeriesWaterfall.callAsync('a','b',(...args)=>{
    log('asyncSeriesWaterfall.callAsync',args)
})

打印结果

2 'undefined.tap' 'a' 'b'
5 '2.tapAsync' 'a' 'b'
2010 '2.tapAsync' 'async.end' 'a' 'b'
2010 'asyncSeriesWaterfall.callAsync' [ '2.tapAsync' ]

回调值2.tapAsync中断了事件执行


const tap =  factory('tap',hooks.asyncSeriesWaterfall)
const tapAsync= factory('tapAsync',hooks.asyncSeriesWaterfall)
const tapPromise= factory('tapPromise',hooks.asyncSeriesWaterfall)


tap('return.1')
tapAsync(undefined,'2',2000);
tapAsync('3',2000);
tapPromise('3000',3000)
// tapPromise.reject('3',4000)
tapAsync(undefined,5000);
tapPromise('1000',1000)
tap('5')
tapAsync('6',500)
hooks.asyncSeriesWaterfall.callAsync('a','b',(...args)=>{
    log('asyncSeriesWaterfall.callAsync',args)
})

打印结果

2 'undefined.tap' 'a' 'b'
4 'undefined.tapAsync' 'a' 'b'
2008 'undefined.tapAsync' 'async.end' 'a' 'b'
2008 '3.tapAsync' '2' 'b'
4013 '3.tapAsync' 'async.end' '2' 'b'
4013 'asyncSeriesWaterfall.callAsync' [ '3.tapAsync' ]

2008 ‘3.tapAsync’ ‘2’ ‘b’ 成功接受到上个事件回传值


以上,为tapable主要的几个钩子的使用方式
总结一下,方便记忆
Sync为同步,
Async为异步
Bai代表可中断事件
Waterfall代表上一事件返回值可作为下一事件的入参
Series串行事件,上一事件执行完毕后才会执行下一事件

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值