Callbacks and SproutCore
Have you ever been writing your SproutCore app, things are going just fine and then you realize that you would like to have a bit more control over your async operations (i.e. Record.refresh(), Record.commit(), Record.destroy() ). Well if you’ve had this feeling, you are not alone. I had this feeling and decided to implement a solution that would not have any harmful effects on the underlying frameworks that make up SproutCore. In this article I will layout my proposal to allow this modification to the underlying frameworks, weighing the pros and cons along the way as well as giving a brief example of how I was able to regain control of my async operation. So let’s get started, shall we…
Proposal ( With pros and cons )
When working with SproutCore you will usually have at least one data-source. This data-source will act as your adapter to the remote services that you app will need to interact with, allowing your app to perform CRUD operations on data.
The data-source is also tied closely to SproutCore’s store for example: If you use the sc-gen command to generate the boiler plate code for your first data-source you will see that the callbacks you are instructed to call are used to notify the ‘store’ that your data-source has finished with the XHRRequest. If you dive a bit deeper you will find that one of the purposes of these callbacks is to set the status of record(s).
If you read the previous paragraph and thought to yourself that you could just use an object controller and observe the status of a record, you would be correct. You should be able to rely on this status to make further decisions. So what’s the catch you say? Well the status property of a record or record array is a publicly accessible property and it can be changed by any other piece of code or any activity on said record.
If you are solely relying on the status of records you could find yourself in a pickle. For example: You refresh a record and once the status has been determined to be READY (or one of it’s many children), you create a nested store from this record. In the time between READY and nested store creation the record is refreshed again from outside code thus placing the record into a BUSY state. You then try to modify this record and receive an exception that the Record is BUSY.
You could say that you could run into this same issue by just using callbacks instead of observers, and you would be correct. Although by using callbacks you can add a bit of safety by having explicit checks for record state changes or modifications and defer you execution plan. Now keep in mind I am not saying that going with the grain of SproutCore is bad.
If you do go with the grain of SproutCore you would have set up an object controller that received the the record as it’s content. You would then observe the status of the controller’s content and make decisions based on it’s status, etc…
Are these extra levels of abstraction really needed just for the cases where you need to refresh, save, or destroy a record? These abstractions end up being extra objects that need to be instantiated and and at least one observer that could have the potential to fire at undesired times if not properly removed. When you start thinking about performance you will begin to ask questions about observers and extra objects taking up memory space. This also becomes a concern when dealing with the dreaded IE. Observers fire very often each counting as one or more statements executed, and IE has a limit and starts spewing long running script errors. Now on to using callbacks…
Callbacks can be useful in the cases stated above as well as many others. I am going to start with an example and then move on to some reasons why callbacks are needed as an addition.
Example:
MyRecord.refresh({
successCallback: function(args) {
// do something useful.
},
errorCallback: function(args) {
// do something useful.
});
In the above example you see that you are able to pass a hash into the record’s refresh function with callback functions. These functions are only executed once in the context of the request. Unlike an observer on the record’s status that will be executed for all of the status transitions, ( there are quite a few.. try one with a console.log() ). If you are using state charts such as the built in StateChart Framework included with SproutCore, you could assign an action that causes a state transition as the receivers of the callback notification. What I am describing here is this, I am able to use the callback functions to avoid an unneeded controller and it’s record status observer. In eliminating these two things I have been able to save memory space and statement executions.
I am not proposing that these callbacks become a replacement for the SproutCore way, but that they can compliment the standard path. By allowing callbacks along side the standard path you are also opening the doors to the rest of the javascript community. The community that thrives by being able to control the flow of their application with callbacks. Take NodeJS, jQuery, etc.. as examples. These developers could instantly feel at home with these callbacks because they already deal with them on a day to day basis.
Hopefully you can see the benefits. So I will now move on to the simple and quick way that I was able to implement these call back with out any side effects to SproutCore. Keep in mind this was the quick path, you could just as easily create a data-source delegate that would provide the same functionality. I am going to link to a gist with the implementation details because Tumblr sucks at code formatting.
https://gist.github.com/741655
If you have given the sample implementation a look over you will see that it’s harmless to SproutCore. When implementing your data-source(s) for your application you will be dealing with the response code from your remote services, this is where you fire the callbacks with any data/information you may want to provide. Examples of data to provide would be the record’s storeKey, id, the service response code, and response messages.
Now that you’ve made it this far you deserve a bonus, so here goes. Would you like to have hooks into the save process that you allow you to execute functions like ‘Record.beforeSave()’ and ‘Record.afterSave()’. You can do this with the implementation in the gist. All you have to do to get these hooks is to add a check for them being defined on the record when you add the ability for callback params to the commit functions of the same files and fire them before and after the actual commit. These little gems can prove to be handy. For example, let’s say that you want to ensure the format of the data before you actually send it to the remote service, then on the way back in from the remote service you want to ensure that it adheres to the model contracts on the client side. If the previous appeals to you, you are not alone and this is just one of the many advantages you will reap from allowing this addition as a compliment to the standard SproutCore path through the data-source/data-store combo.
I have written this proposal in hope that I receive feedback from the fantastic dev community surrounding SproutCore as well as folks who are taking a serious look at SproutCore but felt a bit confused by not have this ability.
1 Notes/ Hide
-
bummerware reblogged this from joshholt
-
joshholt posted this