如何在事件监听器中等待异步调用完成后再执行函数?
嘿,这个场景我之前做项目时也遇到过——没法控制第三方插件的加载/执行顺序,又要在事件触发时等待异步操作完成再执行逻辑。结合你的需求,我整理了几个可行的方案,不用重构原插件也能实现:
方案1:拦截AJAX请求,用自定义事件通知完成
如果你的需求是等待ajax.js发起的特定请求完成后再执行setdata逻辑,可以通过包装jQuery的$.post方法,给目标请求打上标识并触发自定义事件:
// 先缓存原生的$.post方法 const originalPost = $.post; $.post = function(url, data, success, dataType) { // 这里根据ajax.js请求的特征判断(比如特定URL、参数) const isTargetAjaxRequest = url.includes('/ajax-content-load'); // 替换成实际判断条件 // 调用原生方法发起请求 const request = originalPost.apply(this, arguments); // 如果是目标请求,完成后触发自定义事件 if (isTargetAjaxRequest) { request.done(() => { $(document).trigger('ajaxPluginRequestDone'); // 可以存个标记到document上,避免全局变量污染 $(document).data('ajaxPluginHasCompleted', true); }); } return request; };
然后修改你的setdata事件监听器,等待这个自定义事件:
$('body').on('click', '[data-setdata]', function(e) { e.preventDefault(); const $currentElement = $(this); // 先检查请求是否已经完成,避免重复等待 if ($(document).data('ajaxPluginHasCompleted')) { runSetdataLogic($currentElement); return; } // 只监听一次自定义事件,避免多次绑定 $(document).one('ajaxPluginRequestDone', () => { runSetdataLogic($currentElement); }); }); // 把原setdata的逻辑抽成独立函数,方便复用 function runSetdataLogic($element) { // 这里复制原setdata.js里的逻辑,比如: const targetKey = $element.data('setdata'); // ... 你的业务操作 }
方案2:点击时先发起AJAX,用async/await等待完成
如果是点击[data-setdata]元素时,需要主动发起某个AJAX请求,等它完成再执行逻辑,直接用async/await就能搞定:
// 注意:如果原setdata.js已经绑定了监听器,需要阻止它执行 // 这里用直接绑定元素的方式(比body委托优先级高),并阻止事件冒泡 $(document).on('click', '[data-setdata]', async function(e) { // 阻止body上的原监听器触发 e.stopImmediatePropagation(); e.preventDefault(); try { // 先发起你的AJAX请求 const ajaxResponse = await $.post('/your-api-endpoint', { // 请求参数 }); // 请求完成后执行原setdata逻辑 const targetKey = $(this).data('setdata'); // ... 基于ajaxResponse和targetKey的业务操作 } catch (error) { // 处理AJAX失败的情况 console.error('请求失败:', error); } });
方案3:等待所有AJAX请求完成
如果你的需求是等待页面上所有正在进行的AJAX请求都完成,再执行setdata逻辑,可以用jQuery的ajaxStop事件:
$('body').on('click', '[data-setdata]', function(e) { e.preventDefault(); const $currentElement = $(this); // 检查当前是否有活跃的AJAX请求 if ($.active === 0) { runSetdataLogic($currentElement); } else { // 所有请求完成后触发一次 $(document).one('ajaxStop', () => { runSetdataLogic($currentElement); }); } });
注意事项
- 如果原插件的事件监听器是绑定在
body上的委托,直接绑定在元素上的事件会优先触发,所以可以用e.stopImmediatePropagation()阻止原监听器执行。 - 尽量避免全局变量,上面用
$(document).data()存储状态的方式更优雅。
内容的提问来源于stack exchange,提问作者Jaume Mal
相关产品推荐
相关产品推荐

