New Feature: Timers

Speaking of Cocoa, I’ve added a new feature to SproutCore today very much inspired by Cocoa called Timers.

Timers are a better way to execute deferred code than setTimeout() and setInterval(), especially when you need to execute several different methods and you want them to stay in sync, such as during animation.  

The problem with timeouts is that they are both inefficient and non-deterministic.  That is, even if you set a timeout to execute in 100msec, you don’t know when the timeout will actually run.  It may be 100msec, it may be 150msec, it may be 1500msec.  Anything could happen.  To make matters worse, browsers get really slow when they start executing lots of timeouts, meaning the time ranges when your timeouts execute will start to vary even more.

Although we can’t completely overcome the non-deterministic nature of timeouts, Timers at least make it better.  Instead of setting one timeout per Timer, SproutCore sets only one timeout on an object called SC.runLoop.  This object manages all of the timers you have registered in the system.  When its timeout triggers, it will check your timers and fire any of them whose time has come.  Since this is all done in the context of a single timeout handler, timers that are set to expire at the same time will tend to fire together just like you might expect.

In addition to providing a more consistent firing experience, Timers also schedule their time intervals based on when the run loop currently began running.  This means that if you are in an event handler and you create three timers to fire in 100msec, they will all fire at the same time.

Using Timers is extremely simple.  You can easily create them yourself (see the documentation here), or you can use the invokeLater() method defined on both objects an functions.  Here is how you could invoke a method on an object after 100msec:

myObject.invokeLater('updateAnimation', 100) ;

If you just want to invoke a method the next runloop but as soon as possible, you can also omit the time:

myObject.invokeLater('updateAnimation');

Or sometimes you may have a function you’ve defined that you want to execute which is not attached to the object.  You can do that too:

function() {
  // handle animation
}.invokeLater() ;

If you want the function to execute in the context of an object you can do that also:

function() {
  // handle animation
}.invokeLater(myView, 100);

In the past if you wanted your timeout function to execute in the context of a specific object, you had to use bind() which created a nasty closure to do its work.  Thanks to the way timers are implemented, no closures are created during this process unless you pass a bunch of custom parameters (which is not the common case).

If you do want to pass custom parameters though, you can do so:

myObject.invokeLater('updateAnimationTo', 100, 'newState');

That’s it.  Timers are really easy to use and much more performant than setting your own timeouts.
 

Emulating Cocoa in JavaScript

The #1 thing I learned at the JS Meetup in San Francisco last week is that everyone seems to want Cocoa on the web.  I know of at least five frameworks now that try to essentially bring Cocoa to the web.  (Only SproutCore is open source so far afaik).

Cocoa is a great framework.  It makes writing applications on the desktop not only easy but fun.  There are a lot of good reasons to copy Cocoa for the web and a few really bad ones.  Here’s why I think it’s good:

 

  1. Cocoa is hackable.  Cocoa’s greatest strength is that it does everything the right out of the box 80% of the time, but because of how its late-binding object system works, it’s easy to hack it to do pretty much anything that you want.  Other frameworks, especially those with static bindings like Java and C++ are not nearly as easy to hack which is why they instead have to try to do everything, leading me to my next point…
  2. Cocoa is small.  Compared to the Swing APIs or to MFC, Cocoa is really a very small framework.  It has a handful of controls that can be configured to do most of what you want.  Since it is so hackable, Cocoa can get away with this.  Obviously on the web small is important for performance.
  3. Cocoa is clean.  Possibly the best reason to copy Cocoa is that for a 20 year old framework, the Cocoa team has done an outstanding job at keeping their API’s clean and consistent.  Using the same design patterns everywhere in a framework makes a huge difference to its usability.  Once you learn how to do things one way, you can assume they will work that way everywhere.

 

All of these things are good reasons to copy Cocoa for the web.  In many ways it already has built into its design some of the very things that are important to a web-based application.  However, not all in sunshine and roses with Cocoa, especially when it comes to bringing the framework to the web.  For example:

 

  1. Cocoa is built on Objective-C.  And fundamentally, it is built on C, a very static, strong typed language that leaves a lot to the developer to handle, including memory management.  A significant chunk of Cocoa’s functionality (i.e. most of Foundation) goes into overcoming this limitation with C.  Of course, JavaScript is a highly dynamic language with late binding and memory management built right in.  So copying a lot of this to the web is simply wasted space and processor cycles.
  2. Cocoa has cruft.  Like I said, for a 20-year old framework, Cocoa is pretty clean, but it does have some cruft built up.  For example, powerful new features such as bindings and automated garbage collection were just introduced in the last five years.  Bindings in particular make a number of older design patterns still supported by the framework unnecessary.  No doubt, if the team were starting from scratch today, building Cocoa with these new features from the beginning would yield and much smaller more concise framework.

 

So Cocoa is a great framework.  In many ways it has the kinds of things you would want to build apps on the web, which is a great reason to use it as inspiration.  That said, there are a couple of really terrible reasons why you might try to clone Cocoa as well, which unfortunately is what most of the frameworks I’ve seen so far seem to do.  Here’s what I hear from developers a lot of the time:

 

  • I don’t like JavaScript.  It’s true, JavaScript has a bad rep.  But like it or not, JavaScript is the third most widely distributed language on the planet.  It’s built into every web browser and it forms a fundamental part of the fabric of the web.  Not only that, but once you get to know it, JavaScript is actually one of the most dynamic, powerful languages to ever achieve mass adoption.  If you have ever wished you could just build things in smalltalk, objective-c, lisp, ruby, or python in the web browser, then you might be surprised to find most of the stuff you love about these languages is actually in JavaScript.
  • I really love Cocoa.  This comes frequently from people who have been building Cocoa apps for a long time and want to reuse the knowledge they’ve built up for their leap to the web.  Unfortunately, Cocoa was designed to overcome specific limitations with running on desktop machines and writing code in C that simply do not exist in the web world.  The reality is, not everything you learned from Cocoa is relevant for the web.  

Building a Cocoa-like framework for either of these reasons inevitably leads to a framework that tries to clone Cocoa method for method in spite of the fact that it is not being used in the same way.  This yields a framework that works against the grain of the web browser, not with it.  A framework that will do a lot of extra work unnecessarily just to maintain some abstraction, that will in the end break down at some point anyway due to the principle of leaky abstractions.  Ironically, these complications usually end up making Cocoa clones more difficult to use and less fun to code in; exactly the opposite of the intended effect.

Cocoa is a great framework.  It has a lot of lessons to teach us about building applications on the web, but the appropriate response to Cocoa is to be inspired by it, not to copy it.  SproutCore, for example, borrows many design patterns from Cocoa including MVC, Bindings, Delegates, Timers, Run Loops, and more.  But it doesn’t try to clone Cocoa method for method.  For example, SproutCore uses the built in Array class (with some enhancements), instead of trying to have an NSArray and NSMutableArray (which would both have to be mutable anyway).

The idea is to capture the essence of Cocoa (easy and fun to use), while still embracing the native tools of the web (i.e. JavaScipt), to yield a framework that feels at home on the web.  And that is the right way to bring Cocoa to the web.