等待事件被处理以继续执行触发该事件的函数

Waiting for event to be handled to continue executing the function that has triggered the event.

本文关键字:事件 函数 继续 处理 等待 执行      更新时间:2023-09-26

我正在寻找一个优雅,有效的解决方案来解决我的问题:

我有这个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);
}