1.先看个demo,袁老师讲了个例子:以同步方式实现事件监听,咱们使用新的API来实现下,要求是:在循环中等待按钮点击
async function watiClick() {
const el = document.getElementById('btn')
const observer = el.when('click')
let i = 0
async function clicked() {
return new Promise(resolve => {
i++
observer.subscribe(resolve)
})
}
while (true) {
await clicked()
console.log('点击次数', i);
console.log('clicked')
}
}
watiClick()
image.png
2.用法:const observer = eventTarget.when(eventType);
- eventTarget: 任何继承自 EventTarget 的对象,如 HTMLElement、Window、XMLHttpRequest、Web Socket 等。
- eventType: 字符串,表示要监听的事件类型(如 'click'、'load'、'input')
-
observer: Observer实例对象,包含方法如图:
observer实例
3.实例方法
-
subscribe: 事件触发后通过该方法订阅了才能获得结果,
observer.subscribe(参数1,参数2)
,参数2需要是一个SubscribeOptions对象,具体内容目前不知,参数1可以是一个回调方法也可以是个含next和error的对象
observer.subscribe(event=> {
console.log(event,'事件触发了') // 事件对象
})
observer .subscribe({
next(event) {
console.log(event, '事件触发了') // 事件对象
},
error(err) {
console.log(err);
}
})
image.png
既然订阅了事件那么就应该有取消订阅的方法,如const unSubscribe = observer.subscribe(...)
,可惜没有,unSubscribe为undefined
,subscribe没有返回值,那就不能取消订阅了吗?当然不是,别忘了还有takeUntil
方法,后面讲。
- filter: 过滤事件,按钮点击后会触发filter方法,同时打印触发次数time,time从0开始递增,当满足条件(按住Shift键的点击),subscribe方法的回调会触发
button.when('click')
.filter((event, time) => {
console.log(time);
return event.shiftKey
}) // 只处理按住Shift键的点击
.subscribe(event => {
console.log('Shift + Click!',);
});
image.png
- map: 转换事件
button.when('click')
.map((event, time) => {
console.log(time + '点击的次数')
return { dx: event.clientX, dy: event.clientY }
})
.subscribe(result => {
console.log(result);// {dx: 30, dy: 22}
});
image.png
-
every: 事件触发满足条件
e.clientY < 100
则不会打印res,不满足条件后才会打印res,同时该方法不会再调用了,该方法不会返回observer
,因此不能再链式调用subscribe
const res = await document.when('click')
.every(function (e, time) {
console.log('e.clientY:' + e.clientY)
return e.clientY < 100
},)
console.log('事件已终止,res的结果是:', res);
image.png
-
some: 和every类似,事件触发不满足条件
e.clientX > 500
时会一直调用some方法,除非满足条件则会打印res结果为true
,同时该方法不会再调用了,该方法不会返回observer
,不能再链式调用subscribe
const res = await document.when('click')
.some(function (e, time) {
console.log('e.clientX:' + e.clientX)
return e.clientX > 500
},)
console.log('事件已终止,res的结果是:', res);
image.png
- take: 使用 take(n) 限制事件次数,下面点击事件只会触发3次,结合map方法,能看到触发次数
document.when('click')
.take(3)
.map((e, t) => {
console.log('点击次数: ', t);
return { dx: e.clientX, dy: e.clientY }
})
.subscribe(e => {
console.log(e);
})
image.png
- drop: 使用 drop(n)跳过前 n 个事件,可以单独使用也可以结合take方法使用,其调用位置会影响其他方法的调用
document.when('click')
.take(3)
.drop(1)
.map((e, t) => {
console.log('点击次数: ', t);
return { dx: e.clientX, dy: e.clientY }
})
.subscribe(e => {
console.log(e);
})
上面drop
方法在take
方法和map
方法中间调用,第一点击事件触发,不会触发map
和subscribe
,后面两次会触发
9f9a496f-c723-4467-a1b6-9bbefa7b85e8.png
document.when('click')
.drop(1)
.take(3)
.map((e, t) => {
console.log('点击次数: ', t);
return { dx: e.clientX, dy: e.clientY }
})
.subscribe(e => {
console.log(e);
})
上面drop
在take
之前调用,第一点击事件触发,但不影响后两个方法的触发,后面还是会触发3次事件,等于总共点击了4次,第一次被丢弃了
e5706027-2f20-460c-9ae2-2d35274a9215.png
document.when('click')
.take(3)
.map((e, t) => {
console.log('点击次数: ', t);
return { dx: e.clientX, dy: e.clientY }
})
.drop(1)
.subscribe(e => {
console.log(e);
})
上面drop
放在了map
后面,map会触发3次回调,subscribe只会触发2次
image.png
- takeUntil: 使用 takeUntil() 控制订阅,下面例子点击3次后不再订阅点击事件
function handleClick() {
const stopSignal = new EventTarget();
let i = 0
document.when('click')
.takeUntil(stopSignal.when('stop'))
.map((e, t) => {
console.log('点击次数: ', t);
return { dx: e.clientX, dy: e.clientY }
})
.subscribe(e => {
i++
console.log('i', i);
if (i === 3) {
console.log('停止订阅');
stopSignal.dispatchEvent(new Event('stop'));
}
console.log(e);
})
}
handleClick()
image.png
-
first:
first
调用后只会触发一次事件并返回结果,即使调用了take(3)
希望触发三次,但最后调用了first()
最终只会触发一次,该方法不会返回observer
,因此不能再链式调用subscribe
const res = await document.when('click').take(3)
.map(e => ({ dx: e.clientX, dy: e.clientY }))
.first()
console.log('点击后只会触发一次:', res);
image.png
-
last:
last
调用只会在最后一次触发事件返回结果,结合take
方法使用能看到效果,该方法不会返回observer
,因此不能再链式调用subscribe
const res = await document.when('click').take(3)
.map((e, t) => {
console.log('点击次数:', t);
return ({ dx: e.clientX, dy: e.clientY })
})
.last()
console.log('点击3次后打印结果为:', res);
image.png
-
find: 此方法在找到满足的条件后才会返回结果,返回结果后事件不再触发,该方法
不会返回observer
,因此不能再链式调用subscribe
const observer = document.when('click');
const e = await observer.find((event, t,) => {
console.log('点击次数:', t);
return event.target.id === 'btn'
})
console.log(e.target);
image.png