STEW: implementing a Twitter service

Implement a Twitter service to fetch data from Twitter.

The TwitterService object that fetches data from Twitter is located in the TwitterService.js file. A REST (Representational State Transfer) API call is created by constructing a URL with the appropriate Twitter method name and parameters. The credentials are encoded in the URL to log users into Twitter automatically. A JSON (JavaScript Object Notation) response is requested and the maximum number of returned responses is set. The URL is passed to the _doRequest function.

TwitterService.prototype.getFriendsTweets = function( count, progressId ) {
	// Build URL and do the request.
	var url = "http://" + this.username + ":" + this.password + "@twitter.com/statuses/friends_timeline.json?count=" + count;
	this._doRequest( url );
}

The _doRequest function makes an asynchronous request to the URL. The function also sets the type of the request to GET or POST. Before making the actual request by calling the XMLHttpRequest send function a function is assigned to the XMLHttpRequest readystatechange event handler.

TwitterService.prototype._doRequest = function( url, type ){
    this.httpReq = new XMLHttpRequest();
	
    var self = this;
    this.httpReq.onreadystatechange = function() { 
		self._readyStateChanged(); 
	};

	// Default to GET HTTP request if none is provided.
	if ( type == null ) {
		type = "GET";
	}
	
    this.httpReq.open( type, url, true );			
    this.httpReq.send( "" );
}

The _readyStateChanged function is the heart of the TwitterService. The readystatechange event is not only triggered when the request is complete so whenever the event handler function gets called, the value of the XMLHttpRequest readyState property must be checked. Value 4 (DONE) indicates that the data transfer has been completed or that something went wrong during the transfer. Because it indicates both successful and unsuccessful requests, the response status of the request must be determined by looking at the status property of the XMLHttpRequest. If the value is 200 (OK), the response text is retrieved from the XMLHttpRequest responseText property. Any other value indicates that an error has occurred during the request.

TwitterService.prototype._readyStateChanged = function() {
    // complete request?
    if ( this.httpReq.readyState == 4 ) {
        // attempt to get response status
        var responseStatus = null;
        try {
            responseStatus = this.httpReq.status;
        } 
		catch (noStatusException) {
			alert( StringTable.Code.twitterServiceNoStatusAlert );
		}
		
		// Check response status.
		if ( responseStatus == 200 ) {
			var res = null;
			
			// If response doesn't contain xml, forward text.
			res = this.httpReq.responseText;

			// Not needed anymore.
			this.httpReq = null;
			this.handleSuccessResponse.call( this, res );
		} else {
			this.handleErrorResponse.call( this, responseStatus );
		}
    }
}

Depending on the response status of the request, either handleSuccessResponse or handleErrorResponse is triggered to notify whoever triggered the request. Since this is all done asynchronously, callback functions are used.

TwitterService.prototype.handleSuccessResponse = function( arg ) {
	// Eval the data.
	var response = eval( "(" + arg  + ")" );

    // Feed fetched and parsed successfully.
	if ( this.onSuccess )
		this.onSuccess.call( this, response );	
}
	
TwitterService.prototype.handleErrorResponse = function( status ) {
	if ( this.onError )
		this.onError.call( this, status );
}

If onSuccess or onError functions are defined, they are called with a response object. For an error response, this object is an integer that indicates the status of the request. For a successful response, before triggering the onSuccess callback, an eval is performed on the response text. This creates JavaScript objects from JSON response text into the response variable, which is then passed as a parameter to the onSuccess callback.