Reputation: 961
I wrapped jQuery's .click()
in this function which returns a Promise:
async function clickPromise(selector) {
return new Promise((resolve, reject) => {
$(selector).click((event) => resolve(event));
});
}
Is there a better way to adapt jQuery's callback API to Promises than wrapping all of them in these functions?
Upvotes: 0
Views: 213
Reputation: 707436
Is there a better way to adapt jQuery's callback API to Promises than wrapping all of them in these functions?
First off, jQuery contains some promise support already for completion notification. If, for example, you're interested in knowing when an animation is done, they have .promise()
which gets a promise that resolves when the fx animation queue for a particular object is done. These type of actions are one-shot notifications which are a good match for how promises work.
jQuery also has built-in promise support for ajax calls and you can use either their callback interface or a promise interface for jQuery ajax.
jQuery does not have any built-in promise support for things that can be recurring events (such as button clicks) because they don't line up directly with the one-shot capability of promises.
So, if you really want to use a promise for the next click on a particular element, then you would have to build your own promise support for that.
The function that you proposed works for some input parameters:
function clickPromise(selector) {
return new Promise((resolve, reject) => {
$(selector).click((event) => resolve(event));
});
}
For example, if the selector only matches one DOM element or it matches multiple DOM elements and you only want the first click that happens on any of those elements and these elements aren't long lasting such that you're calling this over and over again with the same elements (it will build up event listeners over time), then this could work just fine.
Personally, if you were going to do it this way, I'd suggest this:
// Get promise for the very next click on any of 1 to many items
// resolves when the first click happens
// rejects if the selector doesn't match any elements
function clickPromise(selector) {
return new Promise((resolve, reject) => {
const items = $(selector);
if (!items.length) {
reject(new Error("selector empty"));
return;
}
const handler = function(event) {
// remove all event listeners we previously installed so they don't build up
items.off('click', handler);
resolve(event);
}
// install click handlers
items.on('click', handler);
});
}
The main difference with your implementation is that this removes event listeners when the click happens so they don't build up over time if using this over and over on the same elements and it rejects if the selector doesn't match any elements since otherwise it would be a promise that would never resolve.
FYI, this type of functionality doesn't depend upon jQuery. With a few more lines of code, you could make the same functionality with plain DOM Javascript.
If you only ever intend to use this for one element at a time and will only pass a selector that matches exactly one element, then you could use .one()
to make it a little simpler:
// Get promise for the very next click on any of 1 to many items
// resolves when the first click happens
// rejects if the selector doesn't match any elements
function clickPromise(selector) {
return new Promise((resolve, reject) => {
const items = $(selector);
if (items.length === 0) {
reject(new Error("selector empty"));
return;
} else if (items.length > 1) {
reject(new Error("selector matches more than one element"));
return;
}
items.one('click', function(event) {
resolve(event);
});
}
Upvotes: 2