1

I have something similar to this:

function MyObject() {
  var self = this;

  this.callback = function() {
    self.finishParams = Array.prototype.slice.call(arguments);
    self.parent.finish();
  }

  this.start = function() {
    this.currentCallback = this.callback
    this.startFunc.apply(this.startFunc, this.startParams.concat(this.currentCallback));
  }
}

this.startFunc is a function which is something like function(param1, param2, param3, callback) I have no control over this.startFunc except that it will call the callback with some paramaters.

THE PROBLEM

I have a this.currentCallback because I need to be able to cancel the callback. That is, I've already called this.startFunc and need to prevent the callback.

The problem is, MyObject might send another callback (never 2 at a time) but if I don't cancel the first one immediately when I need to, I won't know which one is valid when I get them back! Might be confusing so here's a diagram:

  • Send callback 1 off
  • Need to cancel! Cancel callback A somehow here
  • Send callback 2 off (still say function has callback 1)

By this point, if I didn't cancel A, then when I got the callback back, I wouldn't know which it was. If I DID cancel A, then I know it's B and no one has to worry.

Please tell me if you do not understand :)

DemiPixel
  • 1,768
  • 11
  • 19
  • I think you are approaching it wrongly.... instead of cancelling the current callback... you need to cancel the process that is calling the callback... – Arun P Johny Apr 02 '15 at 00:42
  • Maybe you are looking for debouncing? http://stackoverflow.com/q/15927371/218196 – Felix Kling Apr 02 '15 at 00:43
  • @ArunPJohny there could be other things in the startFunc that can't be cancelled. If you're saying you can cancel the process which only calls the callback, how would I do that and how would I know which to cancel? – DemiPixel Apr 02 '15 at 00:46
  • A callback is just a function. It's up to the function that you give the callback to to decide whether and when to call it. There's no generic API that you can use to control this, it's specific to the application. – Barmar Apr 02 '15 at 00:50
  • If you can't affect `startFunc`... can you control the parameters of the callback? Is there anything in parameters that is unique, or that you can store something unique in? – Amadan Apr 02 '15 at 00:51
  • As an example, `clearInterval` is the way to cancel the callback created with `setInterval`. – Barmar Apr 02 '15 at 00:52
  • As @Barmar said with a great example, `setInterval` will give you a unique ID that it can recognise each interval by in `clearInterval`. If there is any piece of information in parameters that you can use to identify which callback invocation is which, you can exit early from the callback if the identifier is not in a table (or variable, in case of only one active permitted) listing the currently active callbacks. Alternately, don't use always the same function for callback, but make a new closure each time; this way you don't need to rely on parameters to identify the callee. – Amadan Apr 02 '15 at 00:55
  • @Amadan I don't know how startFunc is going to use my callback. It's probably going to call it with some paramaters that I am completely unaware of. – DemiPixel Apr 02 '15 at 00:56
  • If you are providing a callback, you have to know what kinds of parameters it is receiving. That is why I asked. If there is nothing identifiable, then my other suggestion might be applicable (new closure for each callback). – Amadan Apr 02 '15 at 00:57
  • @Barmar I know of the "setInterval" but I can't require the dev to have a "clearInterval" equivalent. – DemiPixel Apr 02 '15 at 00:57
  • @Amadan I'm building a type of framework which will accept startFunc and I will pass a callback. Once I retrieve that, I will pass the response to finish(). I have no idea what the arguments are. – DemiPixel Apr 02 '15 at 00:59

1 Answers1

2

A proof-of-concept of the scheme laid out in the comments: create a new closure for each callback, let callback identify if it is active or not.

function foreignAPIThatStartsACallback(callback) {
  setTimeout(callback, 1000);
}

var activeCallback;

function wrapCallback(callback) {
  var args = Array.prototype.slice.call(arguments, 1);
  var that = this;
  var wrappedCallback = function() {
    if (wrappedCallback == activeCallback) {
      callback.apply(that, args);
    }
  }
  activeCallback = wrappedCallback;
  return wrappedCallback;
}

function myCallback(what, who) {
  console.log(who + " says " + what);
}

foreignAPIThatStartsACallback(wrapCallback(myCallback, "Hello", "Mario"));
foreignAPIThatStartsACallback(wrapCallback(myCallback, "Goodbye", "Luigi"));
// Mario is cancelled when Luigi gets started

With multiple possible actives:

function foreignAPIThatStartsACallback(callback) {
  setTimeout(callback, 1000);
}

var activeCallbacks = {};

function wrapCallback(callback) {
  if (!wrapCallback.count) wrapCallback.count = 0;
  wrapCallback.count++;
  var args = Array.prototype.slice.call(arguments, 1);
  var that = this;
  var wrappedCallback = function() {
    if (wrappedCallback.id in activeCallbacks) {
      cancelCallback(wrappedCallback);
      callback.apply(that, args);
    }
  }
  wrappedCallback.id = wrapCallback.count;
  activeCallbacks[wrapCallback.count] = true;
  return wrappedCallback;
}

function cancelCallback(wrappedCallback) {
  delete activeCallbacks[wrappedCallback.id];
}

function myCallback(what, who) {
  console.log(who + " says " + what);
}


var marioCallback = wrapCallback(myCallback, "Hello", "Mario");
foreignAPIThatStartsACallback(marioCallback);
var luigiCallback = wrapCallback(myCallback, "Goodbye", "Luigi");
foreignAPIThatStartsACallback(luigiCallback);
var daisyCallback = wrapCallback(myCallback, "Mama?", "Peach");
foreignAPIThatStartsACallback(daisyCallback);

cancelCallback(luigiCallback);
// Mario and Daisy go off
Amadan
  • 191,408
  • 23
  • 240
  • 301
  • so wrappedCallback compares itself to activeCallback? – DemiPixel Apr 02 '15 at 01:24
  • Yup. Since each callback is created separately, the simple object identity suffices. However, this is just a sample, there are more ways to do this. The point is, if you can't affect the caller-back, the callback is responsible for knowing whether or not it should process. This wrapper automates the process, but you could do it in a less generic way that is more specific to your needs, too. – Amadan Apr 02 '15 at 01:24