等待事件被处理以继续执行触发该事件的函数
Waiting for event to be handled to continue executing the function that has triggered the event.
我正在寻找一个优雅,有效的解决方案来解决我的问题:
我有这个web应用程序与许多组件;
一个主要组成部分包括许多将随着时间的推移而增长/演变的附加内容。
这个主组件有一个函数,在实际做它应该做的事情之前,它会触发一个事件,以便插件可以监听。
dostg : function () {
$doc.trigger('beforedo');
//do stuff but after event is handled by the addons ?
}
插件代码
$doc.on('beforedo',function(e) {
//do before addon stuff
}
现在那些在做事情之前可能涉及ajax请求或任何需要一些处理时间的事情。
我可以在ajax请求上增加一个计数器,并等待它下降到nil,但我想要的是一个解决方案,只是等待所有处理程序完成他们的工作(因此,无论在那些插件处理程序中正在做什么都可以工作)。
是否有一些奇迹的解决方案来做到这一点,或者我必须忘记在这种情况下的事件,走另一种方式(函数迭代数组,插件将新函数推入数组)?
谢谢你的专业知识!
-------赏金后编辑
向@xbonez &@Sergiu Paraschiv,在提供赏金之前,我应该用我现在使用的解决方案编辑问题(我对解决方案并不完全满意,因此赏金)。
//app
var $doc = $(document.body);
//component
$doc.on({
wait: function (e) { ++$doc.component.handleinprogress; },
addonready: function () {
--$doc.component.handleinprogress;
if ($doc.component.handleinprogress==0) $doc.trigger('readyfordo');
},
readyfordo: function () {
//do stuff after event has been handled by the addons
}
});
$doc.component = {
handleinprogress: 0,
dostg: function () {
$doc.component.handleinprogress = 0;
$doc.trigger('beforedo');
}
};
//in component addon files
$doc.on('beforedo', function (e) {
$doc.trigger('wait');
//do addon stuff
$doc.trigger("addonready");
}
我不满意这个解决方案,因为即使我不需要在插件之前做的东西,我仍然必须添加处理程序来触发addonready(在至少一个插件->所以我失去了灵活性,从组件中添加/删除插件,而不必担心是否readyfordo被触发,要么我必须在每个插件中包括处理程序-大多数-75%-什么都没有)。
在执行一些代码之前等待所有处理程序完成,您应该使用jQuery的deferred
API。你可以这样做:
$.when($.ajax("/page1.php"), $.ajax("/page2.php")).done(function(a1, a2){
// only executes after both ajax calls have completed
});
此外,jQuery的trigger
允许您传递额外的参数。传入一个函数作为回调函数。
你的最终代码应该看起来像这样:
$doc.trigger('beforedo', function() {
// anything here only executes after the event has been handled
});
$doc.on('beforedo',function(e, callback) {
//do before addon stuff
$.when($.ajax("/page1.php"), $.ajax("/page2.php")).done(function(a1, a2){
// at this point, all your AJAX calls have completed
// call the callback function
callback();
});
}
如果需要,当你调用callback()
时,你甚至可以传递任何你可能需要作为参数传递的结果。相应地,也更改.trigger()
调用中的函数签名。
扩展我在下面留下的评论,如果你喜欢,你可以有一个通用的加载器函数:
$.when(load("page1"), load("page2")).done(function(){
// ...
});
function load(file) {
// return the $.ajax promise
return $.ajax(file);
}
:
jQuery递延jQuery.when ()
jQuery。触发器
查看我的小提琴在这里。计算已完成请求的想法很好,这只是构建代码的问题。知道你需要解耦的"模块/插件",这就是我要做的:
var $doc = $(document);
function Main() {
var handlersCount = 0;
var completedHandlers = 0;
function afterDostg() {
console.log('after addon stuff');
}
this.dostg = function() {
$doc.trigger('beforeDo');
};
this.addHandler = function() {
handlersCount++;
};
this.handleCompleted = function() {
completedHandlers++;
if(completedHandlers === handlersCount) {
completedHandlers = 0;
afterDostg();
}
}
}
function Addon1(main) {
main.addHandler();
$doc.on('beforeDo', function(e) {
console.log('addon1 stuff');
main.handleCompleted();
});
}
function Addon2(main) {
main.addHandler();
$doc.on('beforeDo', function(e) {
setTimeout(
function() {
console.log('addon2 stuff');
main.handleCompleted();
},
1000
);
});
}
var main = new Main();
var addon1 = new Addon1(main);
var addon2 = new Addon2(main);
main.dostg();
使用这个方法,插件可以有任何东西在他们,他们只需要通知"主"当他们完成他们需要做的事情。
如果我是你,我会走得更远,在"Main"中提取整个"处理程序"代码,在一个单独的类中实例化为"Main"中的公共属性,其中afterDostg
作为参数。这样你就不会用这样的元数据污染应用代码。
Softlion指出了几点,我同意。
潜在问题
如果您的一个插件同步调用$doc.trigger("addonready");
:
,那么您当前的实现可能会出现一个问题。// addon 1 :
$doc.on('beforedo',function(e){
$doc.trigger('wait');
//do synchronous stuff :
$('body').append('<div class="progressbar"></div>');
console.log('addon 1 initialized');
$doc.trigger("addonready");
}
// addon 2 :
$doc.on('beforedo',function(e){
$doc.trigger('wait');
$.ajax({ ...
complete: function(){
console.log('addon 2 initialized');
$doc.trigger("addonready");
}
});
}
在这种情况下,根据回调函数的解析顺序,您可能会在第一个插件触发addonready
函数之后,在第二个插件触发wait
之前意外触发readyfordo
事件。
您的代码还依赖于这样的假设:您的所有插件总是对每个.trigger('wait')
执行一个.trigger('addonready')
。我不知道你的代码看起来像什么,或者你有多少插件,但确保这是每个可能的执行路径的情况是相当困难的(例如:如果你有n
ajax调用,你是否测试了2^n
失败案例?)
只要你所有的代码都是内部的,你就可以控制它,但对我来说它似乎很脆弱。
jQuery的deferred/Promises: 一个通用的模式是使用jQuery的承诺。jQuery的所有异步调用现在都被封装在promise中,并且该库提供了一个API,允许以一种相当优雅的方式操作这些调用——而且它可能已经在比您自己的代码更多的极端情况下进行了测试。这是我的2美分。
$doc.component = {
initQueue: [], //this array will contain callbacks, each of which
//is expected to return a promise
dostg : function () {
var queue = $doc.component.initQueue;
var promises = [];
var i, fnInit;
for(i=0; i<queue.length; i++){
fnInit = queue[i];
//safeguard, maybe useless :
if (typeof(fnInit) !== 'function') { continue; }
var obj = fnInit();
// we stack together all return values in an array :
promises.push( obj );
}
// wait for all that should be waited for, then trigger your "main" event :
$.when.apply($, promises).then(function(){ $doc.trigger('readyfordo'); });
// $.when sorts out between promises and other results, and waits
// only if some promises are not resolved.
}
};
//in component addon files
$doc.component.initQueue.push(function(){
//do addon stuff
//if waiting for asynch event, return a promise.
//examples :
// jQuery ajax functions already return a Deferred :
return $.ajax({ ... }); // just don't forget the 'return' ...
return $.get(url, {...}, function(){ ... });
//or, if you need a custom promise, Softlion's example :
var deferred = $.Deferred();
doasyncthing(function() { deferred.resolve(); });
return deferred.promise();
});
而不是使用你的beforedo
, wait
, addonready
事件,你有你的插件注册一个函数在initQueue
已知的组件-注意,你可以选择而不是注册回调,如果你的插件不需要一个。
粗略地说:在你的插件中,你用$doc.component.initQueue.push(function(){ ... })
代替$doc.on('beforeDo', function(){ ... })
,如果你需要等待一些东西,你在这个东西周围返回一个承诺。
然后,您可以让$.when()
函数负责将所有内容捆绑在一起并等待应该等待的内容。
UPDATE:实际上,$.when
期望承诺作为单独的参数
如果存储在数组中,则需要调用$.when.apply($, array)
来匹配函数的签名。
$.when(array)
将考虑它的参数(数组)不是Promise,并立即解析。
提琴
您正在使用的解决方案不能正常工作。如果你有2个插件,第一个接收事件并注册"等待",然后同一个插件调用readyfordo。2会怎样呢?它将没有机会init。
你的每个插件必须将自己添加到一个全局数组中,当你抛出事件时,你应该处理数组中没有任何内容的情况,否则你在你的解决方案中使用相同的代码而不使用handleinprogress计数器,只使用数组的长度。
你可能也对promise模式感兴趣(参见jquery文档中的Deferred object),它使得异步等待"事件"变得轻而易举。
http://api.jquery.com/category/deferred-object/http://api.jquery.com/deferred.promise/var plugins = [];
$.someplugin = function() {
plugins.push(this);
this.init = function() {
//do some things synchronously
//start async things. The callback is called when the async thing is finished.
var deferred = $.Deferred();
doasyncthing(function() { deferred.resolve(); });
//return a promise
return deferred.promise();
}
}
打电话者:
function throwEventAndWaitAsync(callback) {
var promises = [];
for(plugin in plugins) {
promises.push(plugin.init());
}
//Wait on all promises asynchronously then call callback()
$.when(promises).then(callback);
}
- 无法在java脚本中调用图像的点击事件函数
- 如何在事件函数中访问窗口实例
- 事件函数完整日历中的多个ajax调用
- 如何使参数在事件函数中工作
- Jquery Onclick 事件函数第二次不起作用
- 宣传单:双击时不要触发点击事件函数
- Bootstrap/JQuery-单选按钮组事件函数
- 将jQuery事件函数应用于新的Knockout.js数组元素
- 防止在元素上触发新事件,直到当前事件函数在jQuery中完成
- 有没有一种方法可以将多个参数传递给主干js中的事件函数
- 用javascript更新事件函数中的全局变量
- Jquery:如何在事件函数之外获取变量
- 将参数传递给事件函数
- postMessage - 多个 postMessage 事件/函数/回调
- 多事件函数中的悬停延迟
- JavaScript 事件函数 - 在声明之前调用
- 使用 jQuery 循环设置事件函数触发器
- 暂停,直到事件/函数使用 jquery 或 JavaScript 完成
- 如何访问 jQuery 事件函数中的对象属性
- 单击事件函数未从花式盒子灯箱内触发