In my client code, I have
const whatMouseDid = await mouseDoesSomething()
with the latter function looking like:
async mouseDoesSomething() {
const mouseUp = (resolve) => {
const handler = (evt) => {
if (evt.button === 0)
resolve("up")
}
return handler
}
const mouseDown = (resolve) => {
const handler = (evt) => {
if (evt.button === 0)
resolve("down")
}
return handler
}
return new Promise((resolve, reject) => {
document.addEventListener('mouseup', mouseUp(resolve))
document.addEventListener('mousedown', mouseDown(resolve))
})
}
Which is already a bit more convoluted than I'd prefer, but still manageable. However, there's a problem - the listeners are never removed. And because I need to pass in a reference to Promise.resolve
, I can't removeEventListener
easily. The only way I can think to do this is to keep a mutable list of handlers and the events, targets, etc they are assigned to, and in the handler function(s), iterate over that list and remove attached handlers. The optional param {once: true}
also won't work because I don't resolve if the button clicked is not the one I want.
This all feels super convoluted, and makes me think I'm just missing the obvious easy way to do this; am I? Or is it really this much of nuisance?
And because I need to pass in a reference to Promise.resolve, I can't removeEventListener easily. The only way I can think to do this is to keep a mutable list
Why so complicated? You create functions in the promise executor, just reference them.
mouseDoesSomething() {
return new Promise((resolve, reject) => {
const mouseUphandler = evt => {
if (evt.button === 0) {
resolve("up")
document.removeEventListener('mouseup', mouseUpHandler)
document.removeEventListener('mousedown', mouseDownHandler)
}
}
const mouseDownhandler = evt => {
if (evt.button === 0) {
resolve("down")
document.removeEventListener('mouseup', mouseUpHandler)
document.removeEventListener('mousedown', mouseDownHandler)
}
}
document.addEventListener('mouseup', mouseUpHandler)
document.addEventListener('mousedown', mouseDownHandler)
})
}
Surely that's lots of code duplication, but you can abstract this again by creating the handlers dynamically - you just need to do it in the scope where the two handlers are declared:
mouseDoesSomething() {
return new Promise((resolve, reject) => {
const makeHandler = dir => evt => {
if (evt.button === 0) {
resolve(dir)
document.removeEventListener('mouseup', mouseUpHandler)
document.removeEventListener('mousedown', mouseDownHandler)
}
}
const mouseUphandler = makeHandler("up")
const mouseDownhandler = makeHandler("down")
document.addEventListener('mouseup', mouseUpHandler)
document.addEventListener('mousedown', mouseDownHandler)
})
}