0

I pass an anonymous function-A as parameter to another function-B. Even after I call B.destroy, function A exists and gets executed, it even as an live anonymous scope to itself. I found this unexpected behavior through debugger. Following is the code.

var  builder.record.verifyExplorer = new builder.VerifyExplorer(
    window.bridge.getRecordingWindow(),
    builder.getScript().seleniumVersion,
    function(step) {
      builder.getScript().addStep(step);
      builder.stepdisplay.update();
      // Don't immediately stop: this would cause the listener that prevents the click from
      // actually activating the selected element to be detached prematurely.
      setTimeout(function() { builder.record.stopVerifyExploring(); }, 1);
      window.bridge.focusRecorderWindow();
    }
  );

I destroy the above function by defining stopVerifyExploring to be

builder.record.stopVerifyExploring = function() {
  builder.record.verifyExploring = false;
  builder.record.verifyExplorer.destroy();
  builder.record.verifyExplorer = null;
  builder.record.continueRecording();
};

Even after verifyExplorer.destroy is called function(step) is live in anonymous scope and gets executed and does all unwanted things.

I have this weird behavior by replacing

  jQuery(frame.document).bind(l, {}, ae.listeners[l], true);

with

  frame.document.addEventListener(l,ae.listeners[l],true);

in verifyExplorer. How does me changing the above piece code lead to unexpected behavior?

All this is part of me fixing a bug in an open source project sebuilder. Also related post

1
  • Functions can be anonymous but not scopes - at least they are not normally described as such. Scopes in javascript are lexical - they are determined by the arrangement of the source code. Commented Apr 6, 2014 at 15:54

1 Answer 1

3

The behavior isn't unexpected at all, you've done nothing to cancel the timer.

If you want to cancel the timer, save the return value of setTimeout and then pass that value into clearTimeout later when/if you want to cancel the timer.

What's going on:

Functions and objects exist for as long as anything has a reference to them. When you do:

setTimeout(function() { builder.record.stopVerifyExploring(); }, 1);

...you're saving a reference to that anonymous function in the browser's timer handling stuff. That function has a reference to the context in which it was created (and all containing contexts). So even if you do verifyExplorer = null, that has no effect on anything that the verifyExplorer object used to refer to at all, nor anything that was referring to that object. It just clears the reference to that object from that specific variable. If there are other outstanding references (and there are), the object is kept in memory.

In this case, if the only outstanding reference is the function you've given to setTimeout, then clearing the timeout (probably in destroy) will release the browser's reference to that function, which releases that function's references to the contexts it closes over, and those things are eligible for garbage collection.

More (on my blog): Closures are not complicated

Sign up to request clarification or add additional context in comments.

1 Comment

Correct. Call clearTimeout in a destroy function passing reference. developer.mozilla.org/en-US/docs/Web/API/Window.setTimeout

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.