-1

I am trying to implement a simple queue for functions that take callbacks. My problem is that in the flush method, "this" is null when the action being performed is some long-running thing (in my case, indexedDB calls). I've never experienced this sort of behavior before, so please educate me on what is going on?

Here's the code:

var Queue = (function () {
    function Queue() {
    };

    Queue.prototype.items = [];
    Queue.prototype.results = [];
    Queue.prototype.add = function (action) {
        this.items.push(action);
    };
    Queue.prototype.complete = function () { };
    Queue.prototype.flush = function () {
        var args = Array.prototype.slice.call(arguments);
        if (args.length > 0) { this.results.push(args); }
        if (this.items.length > 0) {
            var action = this.items.shift();
            action.call(this);
        } else { // Complete, call back multi.
            var results = this.results;
            this.clear();
            this.complete(results);
        }
    };
    Queue.prototype.clear = function () {
        this.items = [];
        this.results = [];
    };

    Queue.create = function () {
        return new Queue;
    };

    return Queue;
})();
Matthew
  • 11,203
  • 9
  • 38
  • 44
  • 3
    Which line? What did you think `this` was, and what did it turn out to be instead? Narrow down the issue to 5-6 lines of code, by removing irrelevant bits (but not just deleting; you may need to refactor around the removals!) – Lightness Races in Orbit Jan 10 '12 at 14:45
  • @LightnessRacesinOrbit Inside the flush method. I expected this to be the instance of Queue, instead it was null. – Matthew Jan 10 '12 at 14:46
  • I'm asking a more generic question than fixing this precise issue: when is this not the instance of the prototype? – Matthew Jan 10 '12 at 14:55
  • If you have a generic question then you should present a generic testcase. Look closely at the meaning of `this` -- it doesn't care about any "prototype". It originally happened to represent the prototype only because it relates to _the owner of the current function_. Obviously, you're in a new function now. – Lightness Races in Orbit Jan 10 '12 at 14:57
  • http://stackoverflow.com/questions/6971456/accessing-global-property-using-this-keyword-inside-a-function – Lightness Races in Orbit Jan 10 '12 at 15:00
  • If Queue doesn't own its methods, how do I get a reference to the Queue instance from within one if its methods? – Matthew Jan 10 '12 at 15:01
  • **Duplicate** http://stackoverflow.com/questions/7395280/what-is-this-referencing-when-it-is-inside-a-prototype – Lightness Races in Orbit Jan 10 '12 at 15:04
  • @Matthew: `this` does point to the instance on which the function was called, but only if you call the function directly on the instance (e.g. `myQueue.flush()`). Have a look at my answer. – Peter-Paul van Gemerden Jan 10 '12 at 15:05

1 Answers1

1

My best guess, based on what you've given us, is that you're passing a reference to the Queue.prototype.flush function object to some asynchronous function so it can be used as a callback. If that's true, flush() is called like this:

function ayncFunction(callback) {
    // Do some stuff
    callback();
}
ayncFunction(myQueue.flush);

... instead of like this:

myQueue.flush();

Because the function is called separately, instead of on an object, this will be set window (unless you're using strict mode, in which case it's undefined).

You can circumvent the problem by wrapping the call to myQueue.flush() in a function, like this:

var myCallback = function() {
    myQueue.flush();
};
ayncFunction(myCallback);

This works because myCallback becomes a closure around myQueue.

Another way to solve the problem is to use bind() to tie the function to its context (i.e. the object on which it should be called).