Colin... since it has been a while since I've contributed anything novel
here, I'd like to offer up some critiques and some code to possibly help you
out on this path you're on (especially since you mention some performance
issues).

What you are specifically talking about (in this portion of this thread
anyway) is automated garbage collection. You have a great start, in that GC
is extremely helpful, however I feel that you are coupling the concept too
tightly with the Event system. In other words, design everything as
generically as possible and you'll be able to apply to many different
scenarios. Event wiring is one concern... garbage collection (of anything)
is another concern... separate them.

Also, consider a situation where your tooltip is in reality some sort of
application dialog (or wizard), with potentially many children widgets in
it, that all have N "disposables", and maybe a table of data with 100+ rows
where each row has a click handler (etc.., point being some arbitrarily
complex object). Under your system, closing the tooltip would set off a boat
load of synchronous dispose calls, therefore preventing any further
application functions until all the teardown is complete, therefore not
making for an entirely scalable solution (evident in your performance
findings).

Below is my Garbage Collector base class, and a very basic specialized
subclass called DisposableGC. I use this system in my widget framework, and
it indeed makes it easier on the developer. Its "asynchronous" (obviously
js's asynch is merely interruption based) operation makes it a performance
booster as well... The same scenario described above no longer blocks the
UI.

(copyright addition because it is part of a system I will eventually be
distributing, at least in part, commercially... sorry about that)


/*
Copyright 2005-2006-2007 Ryan Gahl
This code is provided as-is without warranty. You may use and distribute it
freely as long as this copyright notice remains.
This license of use applies only to the below provided code. Future versions
of the same code may be released by Ryan Gahl
or another approved entity (company for which he is employed) for commercial
distribution.
This license does not apply to any such future derivations, although at such
time you may still continue to use this code.
*/

/**
 * @namespace RWG
 */
var RWG = {};

/**
 * @namespace RWG.GarbageCollection */
RWG.GarbageCollection = {};

/** @class RWG.GarbageCollection.GCBase */
RWG.GarbageCollection.GCBase = Class.create();
RWG.GarbageCollection.GCBase.prototype =
{
    /*
     * @Constructor
     * @param {integer} frequency (optional)
    initialize: function()
    {
        ///
        /// Private member variables (only visible to privileged methods of
this class)
        ///
        var gcArray = [];
        var collectionInProgress = false;
        var manualCollectionInProgress = false;
        var collectionFrequency = arguments.length > 0 ? arguments[0] : 100;

        ///
        /// Public privileged instance methods (have visibility to private
members and only available to instances)
        ///

        /**
         * Called in order to queue an object up for asynchronous garbage
collection (i.e. disposal)
         * @method
         * @memberOf {RWG.GarbageCollection.GCBase}
         * @param {Object} object The object to add to the GC instance's
collection queue.
         */
        this.add = function(object)
        {
            if (object)
            {
                object.inGarbageCollection = true;
                gcArray.push(object);
                if (!collectionInProgress)
                    this.collect(collectionFrequency);
            }
        };

        /**
         * Abstract method with no base implementation (must be overriden)
         * @method
         * @memberOf {RWG.GarbageCollection.GCBase}
         */
        this.cleanup = function() { throw new Error("No implementation has
been provided for the RWG.GarbageCollection.GCBase.cleanup() method (must be
overriden from a subclass)"); };

        /**
         * Tells the garbage collector to start collecting in intervals of
{frequency} milliseconds
         * @method
         * @memberOf {RWG.GarbageCollection.GCBase}
         * @param {Milliseconds} frequency
         */
        this.collect = function(frequency)
        {
            collectionFrequency = frequency;
            if (!manualCollectionInProgress)
            {
                if (gcArray && gcArray.length > 0)
                {
                     collectionInProgress = true;
                    this.cleanup(gcArray[0]);
                    gcArray.splice(0,1);
                }

                if (gcArray && gcArray.length > 0)
                {
                    setTimeout(function()
                    {
                        this.collect(frequency);
                    }.bind(this), frequency);
                }
                else
                    collectionInProgress = false;
            }
        }.bind(this);

        /**
         * Triggers the garbage collector to perform a complete cleanup of
all queued items
         * This would be called, for instance, on application shutdown (or
from window.onclose)
         * @method
         * @memberOf {RWG.GarbageCollection.GCBase)
         */
        this.manualCleanup = function()
        {
            manualCollectionInProgress = true;
            //simply loop through all the array elements and clear them (not
worried about performance in this case)
            if (gcArray)
            {
                var len = gcArray.length;
                for (var i = 0; i < len; i++)
                {
                    this.cleanup(gcArray[i]);

                    gcArray[i] = null;
                }
                gcArray = [];
            }
            manualCollectionInProgress = false;
        }.bind(this);
    }
};

/**
 * DisposableGC is a very basic specialized subclass of the GCBase class.
 * Its purpose is to handle the disposing of object resources
 * from memory asynchronously in the background so-as not to
 * impede application performance.
 * @extends {RWG.GarbageCollection.GCBase}
 */
RWG.GarbageCollection.DisposableGC = Class.create();
RWG.GarbageCollection.DisposableGC.prototype =
{
    /**
     * @constructor
     */
    initialize: function()
    {
        //cache the superclass
        this.base = new RWG.GarbageCollection.GCBase();

        //inherit
        Object.extend(this, this.base);

        //override
        this.cleanup = function(obj)
        {
            if (obj.dispose && typeof obj.dispose == "function")
            obj.dispose();
        };
    }
};

// create a globally available generic GC instance (and optionally set it's
collection frequency to half a second - otherwise default is 100
milliseconds)
var globalGC = new RWG.GarbageCollection.DisposableGC(500);


Now, instead of calling dispose() directly on all those objects (thus
potentially slowing other stuff down unnecessarily), you call .add() on your
app's GC instance, which will quietly do it's business in the background at
the configurable frequency...

{...your code...doing stuff...etc...etc...oops, someObject is done being
useful...}
globalGC.add(someObject);
{...your code continues right along... takes a break... someObject gets
disposed gracefully}

And of course, now you've got a generic design which can be plugged into
various specialized situations. I also have a specialized GC class called
domGC, which handles de-referencing DOM elements between widget request
cycles...

--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"Prototype: Core" group.
To post to this group, send email to prototype-core@googlegroups.com
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/prototype-core?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to