等待处理事件以继续执行触发事件的function。
我正在寻找一个优雅,高效的解决方案来解决我的问题:
我有这个带有许多组件的webapp;
一个主要组成部分包括许多随时间增长/发展的新增内容。
这个主要组件有一个function,在实际执行它应该做的事情之前,它会在事件之前触发事件,以便插件可以监听。
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请求上增加一个计数器并等待它降到零,但我想要的是一个等待所有处理程序完成工作的解决方案(因此,无论在这些插件中做什么都可以工作)处理程序)。
是否有一些奇迹解决方案要做到这一点或我必须忘记在这种情况下的事件,并采取另一种方式(迭代的函数数组,插件将新函数推入数组)?
感谢您的专业知识!
——-编辑后的编辑
向@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()
更改.trigger()
调用中的函数签名。
扩展我留下的评论,如果你愿意,你可以有一个共同的加载器function:
$.when(load("page1"), load("page2")).done(function(){ // ... }); function load(file) { // return the $.ajax promise return $.ajax(file); }
看到:
jQuery推迟了
jQuery.when()
jQuery.trigger
看看我的小提琴。 计算完成请求的想法很好,这只是构造代码的问题。 知道你需要解耦的“模块/插件”,这就是我要去的方式:
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”中的整个“处理程序”代码提取到一个单独的类中,该类在“Main”中实例化为公共属性,并使用afterDostg
作为参数。 这样你就不会用这样的元东西来规范app代码。
Softlion指出了几点,我同意这一点。
潜在问题 :
如果你的一个插件调用$doc.trigger("addonready");
,你当前的实现可能会出现一个问题$doc.trigger("addonready");
同步 :
// addon 1 : $doc.on('beforedo',function(e){ $doc.trigger('wait'); //do synchronous stuff : $('body').append(''); 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
function之后,以及在第二个插件发生更改以触发wait
之前,您可能会意外触发readyfordo
事件。
您的代码还依赖于所有插件将始终为每个.trigger('wait')
执行一个.trigger('addonready')
的.trigger('wait')
。 我不知道您的代码是什么样的,或者您有多少个插件,但确保每个可能的执行路径都是这种情况是一个很大的障碍(例如:如果你有n
你有没有测试过2^n
故障情况? ajax打电话?)
只要你的所有代码都在内部,你就可以控制它,但它对我来说似乎很脆弱。
jQuery的Deferreds / Promises:
通用模式是使用jQuery的承诺。 所有jQuery的异步调用现在都包含在promises中,并且库提供了一个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
您可以使用插件在initQueue
已知的initQueue
注册一个函数,而不是使用您的beforedo
, wait
, addonready
事件 - 请注意,如果您的插件不需要,则可以选择不注册回调。
粗略地说:在您的插件中,用$doc.on('beforeDo', function(){ ... })
替换$doc.on('beforeDo', function(){ ... })
$doc.component.initQueue.push(function(){ ... })
,如果你我需要等待一些东西,你会对这件事做出承诺。
然后,您可以让$.when()
函数负责将所有内容捆绑在一起并等待应该等待的内容。
更新 :实际上, $.when
期望将promises作为单独的参数
如果存储在数组中,则需要调用$.when.apply($, array)
以适合函数的签名。
$.when(array)
会认为它的参数(数组)不是Promise,并立即解析。
小提琴
您正在使用的解决方案无法正常工作。 如果你有2个插件,第一个接收事件并注册’wait’,然后同一个调用readyfordo。 第二次会发生什么? 它没有机会初始化。
每个插件都必须在全局数组中添加自己,当你抛出事件时,你应该处理数组中什么都没有的情况,否则你在解决方案中使用相同的代码而不使用handleinprogress计数器,只需使用长度而不是数组。
您可能还对promise模式感兴趣(请参阅jquery doc中的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); }
就如此容易。