简体   繁体   中英

Javascript event execution deferred in a queue

I'm trying to explain my problem to know the better way to solve it. I've searching a bit, but I don't know how to search exactly:

  • I have an HTML page with three areas: Panel A, Grid B and Grid C.
  • On grid C, I can do an action on a row (only clicking it) that updates some counters on panel A and Grid B, but they're calculated on database totals.
  • When I do the row action I update the row immediately and trigger an event listened by Panel A and Grid B which sends both requests against the server to update it's counters.

Every row update is a bit heavy and if the user clicks various rows fast, the javascript execution is locked flooding the server with updates of Panel A and Grid B which could be deferred to execute only one time if on 1 or 2 seconds the event is not triggered.

I would solve the problem on the listenTo callback because it could be another panel that the event action must be performed "immediately".

I imagine something like this (only refresh after 2 seconds of no event listened), but I think that there must be a better way:

var eventTimeout = {}; // one for listener
element.bind('eventName' function() {
    if (eventTimeout['eventName']) {
        clearTimeout(eventTimeout['eventName']); // I understand that if the timeout has been exhausted no error is thrown
    }
    eventTimeout['eventName'] = 
        setTimeout(function() {
            eventTimeout['eventName'] = null;
            doAction(); 
        }, 2000);
});

I'll go away with that implementation (I haven't tested yet), when I have more time, I'll put it on a JSFiddle to help to understand.

You are on the right track with your code but you may want to use something like lodash-throttle function decorators rather than reinventing the wheel here IMO.

lodash Throttle

Creates a throttled function that only invokes func at most once per every wait milliseconds. The throttled function comes with a cancel method to cancel delayed invocations. Provide an options object to indicate that func should be invoked on the leading and/or trailing edge of the wait timeout. Subsequent calls to the throttled function return the result of the last func call.

examples from their own site:

 // avoid excessively updating the position while scrolling jQuery(window).on('scroll', _.throttle(updatePosition, 100)); // invoke `renewToken` when the click event is fired, but not more than once every 5 minutes jQuery('.interactive').on('click', _.throttle(renewToken, 300000, { 'trailing': false })); // cancel a trailing throttled call jQuery(window).on('popstate', throttled.cancel);

Using the previous @bhantol very valuable response, and some other stackoverflow responses ( https://stackoverflow.com/a/43638411/803195 ) I've published a sample code that simulates the behavior I actually want.

Perhaps it was not well defined on initial question, but I need actually use debounce and it must be dynamic, depending on some variables (a checkbox on the following sample) it must be "delayed" or "immediate":

https://codepen.io/mtomas/pen/xYOvBv

var debounced = _.debounce(function() {
  display_info($right_panel);
}, 400);
$("#triggerEvent").click(function() {
  if (!$("#chk-immediate").is(":checked")) {
    debounced();
  } else {
    display_info($right_panel, true);
  }
});

The sample is based on a original sample published on that (interesting) article:

https://css-tricks.com/debouncing-throttling-explained-examples/

-- UPDATE -- Using debounce of lodash implies me to import full lodash (72Kb minimized), so I've implemented a "lite" own debounce using this reference:

https://davidwalsh.name/function-debounce

function debounce(func, wait, immediate) {
    var timeout;
    return function() {
        var context = this, args = arguments;
        var later = function() {
            timeout = null;
            if (!immediate) func.apply(context, args);
        };
        var callNow = immediate && !timeout;
        clearTimeout(timeout);
        timeout = setTimeout(later, wait);
        if (callNow) func.apply(context, args);
    };
};

I've updated my codepen test too.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM