I am writing a Javascript library and I would like to know if it is possible to do the following.
I want to trigger a custom event on an element, but I don't know a priori which event handler(s) have been subscribed to this event, nor how many. Then, I would like to wait for all these event handlers to complete and then check if any of them has perform a given action (e.g. "reject" the event). If not, then the function that triggers the event shall proceed.
To be clear, I can provide arguments to the event handler(s), such as a "() => reject()" function, or define any sort of "contract" for the event handler, but I cannot modify the code that subscribes the event handler(s). Such code would be written by the users of the library.
Is this possible / desirable?
Thanks!
Update
Here is an example of code snipper I would like to use, consider that library end-user would essentially call addEventListener() or $.on() by themselves
$body = $("body")
function rejectEvent(o) {
o.reject();
}
function acceptEvent(o) {}
function triggerEvent() {
let isRejected = false;
$body.trigger('custom-event', {
reject: () => isRejected = true;
});
// Wait for all event handlers to complete...
if (isRejected) {
console.log('stop');
} else {
console.log('proceed');
}
}
triggerEvent(); // Should display 'proceed'
$body.on('custom-event', function(e, o) {
console.log('do nothing');
});
triggerEvent(); // Should display 'do nothing' then 'proceed'
$body.on('custom-event', function(e, o) {
console.log('reject');
o.reject();
});
triggerEvent(); // Should display 'do nothing' then 'reject' then 'stop'
$body.off('custom-event');
$body.on('custom-event', async function(e, o) {
setTimeout(() => {
console.log('reject');
o.reject();
}, 5000);
});
triggerEvent(); // Should display 'proceed' then 'reject'
As shown by this example, I can correctly retrieve the reject status of the event handlers as long as the event handlers are executed synchronously (at least that what I understood from googling this topic). However, the main issue I have is if the end-user defines the event handlers as asynchronous.
So far, the best option I can see is to document that async event handlers are not supported, but I would love to be able to support them as well.
From the above comments ...
And of cause it can be done.
One would start with an intercepting approach for
HTMLElement.prototype.addEventListenerwhere one would wrap additional functionality around the original implementation. Thus one later is capable to also control the dispatching of custom events by either replacingHTMLElement.prototype.dispatchEventwith an own implementation or comes up with a custom e.g.HTMLElement.prototype.dispatchMonitoredEventmethod or, even better, in case one entirely controls the dispatching environment, one implements it as just to the lib-author accessibledispatchMonitoredEventmethod.As for the interception approach. One does this in order to store element-node specific listeners in a (maybe even globally accessible)
listenerStoragewhich is aWeakMapinstance which, based on node references, holds each node's listenerMapinstance. The latter features event-type specific entries, where one can access the handler-array value by an event-type key.The monitoring depends on either handler functions which have to return a value (which is not the classic handler function way) from which one could tell failure/success or on return values of resolved/settled async handler functions (which is even more unusual). Building on the above example code, the implementation and usage of a non/prototypal async
dispatchMonitoredEventfunction/method could look like follows ...Edit
Having learned from the above approach and implementation and taking now the OP's later provided example code into account, one could come up with an implementation for custom cancelable events where the entire
async...awaithandling is build around a custom event's augmenteddetailproperty.Thus, regardless of having registered normal/classic or async handler functions, one does not change a handlers arity (the amount of a function's expected / to be handled arguments) but does control the cancellation of a dispatch process by setting e.g. the sole event-argument's
detail.proceedproperty tofalse.All of an element's registered event-type specific handler function's will be processed by an async generator which controls a functions execution and has access to the current event object's reference. Based on either failing/rejected handler functions or on the current
event.detail.proceedvalue, the async generator continues/stops yielding.The asynchronous
dispatchCustomCancelableEventmethod which operates the generator does return an object which carries all relevant data, likesuccess,canceled,errorandevent, about the settled dispatch process. Andevent.detailprovides additional information about the involvedhandlersand thecancelHandler(the handler which was responsible for any kind of cancelation).