| Posted by kyle on January 5, 2010 11:07 PM | bookmark / share: |
|
The responseText attribute must return the result of running these steps:
1. If the state is not LOADING or DONE return the empty string and terminate these steps.
2. Return the text response entity body.
To rephrase for my purposes, responseText should return the intermediate contents of the response when an XMLHttpRequest is interrogated during the LOADING state. It'll take a little work to handle these partial responses as valid script, but let's first address browser support. Firefox and Webkit browsers already support this behavior if you set the Content-Type header of your response correctly. IE8 throws an exception when responseText is accessed before readyState reaches COMPLETE.
I ran a modified version of the streaming response tests I used in my last post to verify progressive XHR handling. The server returns several chunks in 100ms intervals that include script that indicates how much of the response was received before it is first handled by the browser.
| Bytes Buffered | |||
| Configuration | Firefox 3.5 | Chrome 3.0 | IE 8 |
| Tranfer-Encoding: chunked | 111 | 536 | N/A |
|
Content-Type: text/html Tranfer-Encoding: chunked |
111 | N/A | N/A |
|
Content-Type: text/plain Tranfer-Encoding: chunked |
111 | 85 | N/A |
|
Content-Type: application/x-javascript Tranfer-Encoding: chunked |
111 | 111 | N/A |
For Webkit browsers, it's critical to specify a Content-Type of "text/plain" or "application/x-javascript" when returning script content to an XHR for progressive handling. Seems reasonable, but it's easy to neglect. In my testing, I didn't see any change in behavior in the presence of a "charset" param.
Note that Microsoft's documentation for XMLHttpRequest now refers the to draft specification. I'm hopeful that we'll be seeing support for progressive responses soon.
Now, since we'll be interpreting partial response content as executable script, we'll need to do something to ensure that each chunk we evaluate terminates on a complete expression. For this test, I added delimiters between valid blocks of source:
window.aFunction(); // -- // window.bFunction(); // -- //
Where //--// is the delimiter. When outputting using chunked transfer encoding, you might organize code so that a delimiter is present at the end of each chunk boundary. On each readyState change, if the state is LOADING or DONE, I call a function to read the new content, identify a safe place to trim it, and append it to a buffer.
var index = 0;
var buffer = '';
var DELIMITER = '//--//';
function handlePartialResponse(request) {
var i = request.responseText.lastIndexOf(DELIMITER);
if (i > index) {
i += DELIMITER.length;
var newChunk = request.responseText.substr(index, (i - index));
buffer += newChunk;
index = i;
flushBuffer();
}
}
Finally, we evaluate the contents of the buffer. It's not necessary to remove the delimiter, since it's a valid JavaScript comment.
function flushBuffer() {
window.eval(buffer);
buffer = '';
}
What would you use this for? Consider this technique for the response channel in your next Comet app or any time you're able to deliver part of a script response while doing expensive server side work to produce the rest.
